Enhance project structure by updating .gitignore, adding new skills to skills-lock.json, and implementing contributors dialog in main.tsx. Improve scroll reveal functionality in scroll-reveal.tsx, and add new sections to index.html with corresponding styles in SASS files. Include new SVG assets and update main.css for better styling consistency.
421
.agents/skills/svg-animations/SKILL.md
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
---
|
||||
name: svg-animations
|
||||
description: Create beautiful, performant SVG animations and illustrations. Use this skill when the user asks to create SVG graphics, icons, illustrations, animated logos, path animations, morphing shapes, loading spinners, or any animated SVG content. Covers SMIL animations, CSS-driven SVG animation, path drawing effects, shape morphing, motion paths, gradients, masks, and filters.
|
||||
---
|
||||
|
||||
This skill guides creation of handcrafted SVG animations — from simple animated icons to complex multi-stage path animations. SVGs are a markup language for images; every element is a DOM node you can style, animate, and script.
|
||||
|
||||
## SVG Fundamentals
|
||||
|
||||
### Coordinate System
|
||||
SVGs use a coordinate system defined by `viewBox="minX minY width height"`. The viewBox is your canvas — all coordinates are relative to it, making SVGs resolution-independent.
|
||||
|
||||
```svg
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- 200x200 unit canvas, scales to any size -->
|
||||
</svg>
|
||||
```
|
||||
|
||||
### Shape Primitives
|
||||
|
||||
```svg
|
||||
<rect x="10" y="10" width="80" height="40" rx="4" fill="#1a1a1a" />
|
||||
<circle cx="50" cy="50" r="30" fill="#e63946" />
|
||||
<ellipse cx="50" cy="50" rx="40" ry="20" fill="#457b9d" />
|
||||
<line x1="10" y1="10" x2="90" y2="90" stroke="#2a9d8f" stroke-width="2" />
|
||||
<polygon points="50,5 95,90 5,90" fill="#e9c46a" />
|
||||
<polyline points="10,80 40,20 70,60 100,10" fill="none" stroke="#264653" stroke-width="2" />
|
||||
```
|
||||
|
||||
### The `<path>` Element — The Power Tool
|
||||
|
||||
The `d` attribute defines a path using commands. Uppercase = absolute, lowercase = relative.
|
||||
|
||||
| Command | Purpose | Syntax |
|
||||
|---------|---------|--------|
|
||||
| M/m | Move to | `M x y` |
|
||||
| L/l | Line to | `L x y` |
|
||||
| H/h | Horizontal line | `H x` |
|
||||
| V/v | Vertical line | `V y` |
|
||||
| C/c | Cubic bézier | `C x1 y1, x2 y2, x y` |
|
||||
| S/s | Smooth cubic bézier | `S x2 y2, x y` |
|
||||
| Q/q | Quadratic bézier | `Q x1 y1, x y` |
|
||||
| T/t | Smooth quadratic | `T x y` |
|
||||
| A/a | Elliptical arc | `A rx ry rotation large-arc sweep x y` |
|
||||
| Z/z | Close path | `Z` |
|
||||
|
||||
**Cubic Bézier** (`C`): Two control points define the curve. The first control point sets the departure angle, the second sets the arrival angle.
|
||||
|
||||
```svg
|
||||
<path d="M 10 80 C 40 10, 65 10, 95 80" stroke="#000" fill="none" stroke-width="2" />
|
||||
```
|
||||
|
||||
**Smooth Cubic** (`S`): Reflects the previous control point automatically — perfect for chaining fluid S-curves.
|
||||
|
||||
```svg
|
||||
<path d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="#000" fill="none" />
|
||||
```
|
||||
|
||||
**Arc** (`A`): `rx ry x-rotation large-arc-flag sweep-flag x y`
|
||||
- `large-arc-flag`: 0 = small arc, 1 = large arc (>180°)
|
||||
- `sweep-flag`: 0 = counterclockwise, 1 = clockwise
|
||||
|
||||
```svg
|
||||
<!-- Heart shape using arcs and quadratic curves -->
|
||||
<path d="M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 Z"
|
||||
fill="#e63946" />
|
||||
```
|
||||
|
||||
### Grouping and Transforms
|
||||
|
||||
```svg
|
||||
<g transform="translate(50, 50) rotate(45)" opacity="0.8">
|
||||
<rect x="-20" y="-20" width="40" height="40" fill="#264653" />
|
||||
</g>
|
||||
```
|
||||
|
||||
Use `<g>` to group elements for collective transforms, styling, and animation targets.
|
||||
|
||||
### Gradients, Masks, and Filters
|
||||
|
||||
```svg
|
||||
<defs>
|
||||
<!-- Linear gradient -->
|
||||
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#e63946" />
|
||||
<stop offset="100%" stop-color="#457b9d" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Radial gradient -->
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fff" stop-opacity="0.8" />
|
||||
<stop offset="100%" stop-color="#fff" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- Mask -->
|
||||
<mask id="reveal">
|
||||
<rect width="100%" height="100%" fill="black" />
|
||||
<circle cx="100" cy="100" r="50" fill="white" />
|
||||
</mask>
|
||||
|
||||
<!-- Blur filter -->
|
||||
<filter id="blur">
|
||||
<feGaussianBlur in="SourceGraphic" stdDeviation="3" />
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<rect width="200" height="200" fill="url(#grad)" />
|
||||
<rect width="200" height="200" fill="url(#grad)" mask="url(#reveal)" />
|
||||
<circle cx="50" cy="50" r="20" filter="url(#blur)" fill="#e63946" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSS Animations on SVG
|
||||
|
||||
Many SVG attributes are valid CSS properties: `fill`, `stroke`, `opacity`, `transform`, `stroke-dasharray`, `stroke-dashoffset`, etc.
|
||||
|
||||
### Basic CSS Animation
|
||||
|
||||
```css
|
||||
.pulse {
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.15); opacity: 0.7; }
|
||||
}
|
||||
```
|
||||
|
||||
### Stroke Drawing Animation (The Classic)
|
||||
|
||||
The most iconic SVG animation. Uses `stroke-dasharray` and `stroke-dashoffset` to make a path appear to draw itself.
|
||||
|
||||
**How it works:**
|
||||
1. Set `stroke-dasharray` to the path's total length (one giant dash + one giant gap)
|
||||
2. Set `stroke-dashoffset` to the same length (shifts the dash off-screen)
|
||||
3. Animate `stroke-dashoffset` to 0 (slides the dash into view)
|
||||
|
||||
```svg
|
||||
<svg viewBox="0 0 200 200">
|
||||
<path class="draw" d="M 20 100 C 20 50, 80 50, 80 100 S 140 150, 140 100"
|
||||
fill="none" stroke="#1a1a1a" stroke-width="3" />
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.draw {
|
||||
stroke-dasharray: 300;
|
||||
stroke-dashoffset: 300;
|
||||
animation: draw 2s ease forwards;
|
||||
}
|
||||
@keyframes draw {
|
||||
to { stroke-dashoffset: 0; }
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Getting exact path length in JS:**
|
||||
```js
|
||||
const path = document.querySelector('.draw');
|
||||
const length = path.getTotalLength();
|
||||
path.style.strokeDasharray = length;
|
||||
path.style.strokeDashoffset = length;
|
||||
```
|
||||
|
||||
### Staggered Multi-Path Drawing
|
||||
|
||||
```css
|
||||
.line-1 { animation-delay: 0s; }
|
||||
.line-2 { animation-delay: 0.3s; }
|
||||
.line-3 { animation-delay: 0.6s; }
|
||||
```
|
||||
|
||||
### CSS `d` Property Animation
|
||||
|
||||
Modern browsers support animating the `d` attribute directly in CSS:
|
||||
|
||||
```css
|
||||
path {
|
||||
d: path("M 10,30 A 20,20 0,0,1 50,30 A 20,20 0,0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 z");
|
||||
transition: d 0.5s ease;
|
||||
}
|
||||
path:hover {
|
||||
d: path("M 10,50 A 20,20 0,0,1 50,10 A 20,20 0,0,1 90,50 Q 90,80 50,100 Q 10,80 10,50 z");
|
||||
}
|
||||
```
|
||||
|
||||
**Requirement:** Both paths must have the same number and types of commands for interpolation to work.
|
||||
|
||||
---
|
||||
|
||||
## SMIL Animations (Native SVG)
|
||||
|
||||
SMIL animations are declared directly inside SVG markup. They work even when SVG is loaded as an `<img>` or CSS `background-image` — where CSS and JS can't reach.
|
||||
|
||||
### `<animate>` — Animate Any Attribute
|
||||
|
||||
```svg
|
||||
<circle cx="50" cy="50" r="20" fill="#e63946">
|
||||
<animate attributeName="r" from="20" to="40" dur="1s"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
```
|
||||
|
||||
With keyframes:
|
||||
```svg
|
||||
<animate attributeName="cx"
|
||||
values="50; 150; 100; 50"
|
||||
keyTimes="0; 0.33; 0.66; 1"
|
||||
dur="3s" repeatCount="indefinite" />
|
||||
```
|
||||
|
||||
### `<animateTransform>` — Transform Animations
|
||||
|
||||
```svg
|
||||
<rect x="-20" y="-20" width="40" height="40" fill="#264653">
|
||||
<animateTransform attributeName="transform" type="rotate"
|
||||
from="0" to="360" dur="4s" repeatCount="indefinite" />
|
||||
</rect>
|
||||
```
|
||||
|
||||
Types: `translate`, `scale`, `rotate`, `skewX`, `skewY`
|
||||
|
||||
### `<animateMotion>` — Move Along a Path
|
||||
|
||||
```svg
|
||||
<circle r="5" fill="#e63946">
|
||||
<animateMotion dur="3s" repeatCount="indefinite" rotate="auto">
|
||||
<mpath href="#motionPath" />
|
||||
</animateMotion>
|
||||
</circle>
|
||||
<path id="motionPath" d="M 20,50 C 20,0 80,0 80,50 S 140,100 140,50"
|
||||
fill="none" stroke="#ccc" />
|
||||
```
|
||||
|
||||
`rotate="auto"` orients the element tangent to the path. `rotate="auto-reverse"` flips it 180°.
|
||||
|
||||
### `<set>` — Discrete Value Changes
|
||||
|
||||
```svg
|
||||
<rect width="40" height="40" fill="#264653">
|
||||
<set attributeName="fill" to="#e63946" begin="1s" />
|
||||
</rect>
|
||||
```
|
||||
|
||||
### Timing and Synchronization
|
||||
|
||||
```svg
|
||||
<!-- Chain animations by referencing IDs -->
|
||||
<animate id="first" attributeName="cx" to="150" dur="1s" fill="freeze" />
|
||||
<animate attributeName="cy" to="150" dur="1s" begin="first.end" fill="freeze" />
|
||||
<animate attributeName="r" to="30" dur="0.5s" begin="first.end + 0.5s" fill="freeze" />
|
||||
```
|
||||
|
||||
Trigger values:
|
||||
- `begin="click"` — on click
|
||||
- `begin="2s"` — after 2 seconds
|
||||
- `begin="other.end"` — when another animation ends
|
||||
- `begin="other.end + 1s"` — 1s after another ends
|
||||
- `begin="other.repeat(2)"` — on 2nd repeat of another
|
||||
|
||||
### Easing with `calcMode` and `keySplines`
|
||||
|
||||
```svg
|
||||
<animate attributeName="cx" values="50;150" dur="1s"
|
||||
calcMode="spline" keySplines="0.42 0 0.58 1" />
|
||||
```
|
||||
|
||||
`calcMode` options: `linear` (default), `discrete`, `paced`, `spline`
|
||||
|
||||
`keySplines` takes cubic-bezier control points (x1 y1 x2 y2) per interval. Common easings:
|
||||
- Ease-in-out: `0.42 0 0.58 1`
|
||||
- Ease-out: `0 0 0.58 1`
|
||||
- Bounce-ish: `0.34 1.56 0.64 1`
|
||||
|
||||
### Shape Morphing with SMIL
|
||||
|
||||
Both shapes must have identical command structures (same number of points, same command types):
|
||||
|
||||
```svg
|
||||
<path fill="#e63946">
|
||||
<animate attributeName="d" dur="2s" repeatCount="indefinite"
|
||||
values="M 50,10 L 90,90 L 10,90 Z;
|
||||
M 50,90 L 90,10 L 10,10 Z;
|
||||
M 50,10 L 90,90 L 10,90 Z" />
|
||||
</path>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Patterns & Recipes
|
||||
|
||||
### Loading Spinner
|
||||
|
||||
```svg
|
||||
<svg viewBox="0 0 50 50">
|
||||
<circle cx="25" cy="25" r="20" fill="none" stroke="#1a1a1a"
|
||||
stroke-width="3" stroke-linecap="round"
|
||||
stroke-dasharray="90 150" stroke-dashoffset="0">
|
||||
<animateTransform attributeName="transform" type="rotate"
|
||||
from="0 25 25" to="360 25 25" dur="1s"
|
||||
repeatCount="indefinite" />
|
||||
<animate attributeName="stroke-dashoffset" values="0;-280"
|
||||
dur="1.5s" repeatCount="indefinite" />
|
||||
</circle>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### Animated Checkmark
|
||||
|
||||
```svg
|
||||
<svg viewBox="0 0 52 52">
|
||||
<circle cx="26" cy="26" r="24" fill="none" stroke="#4caf50"
|
||||
stroke-width="2" class="draw"
|
||||
style="stroke-dasharray:150;stroke-dashoffset:150;
|
||||
animation:draw .6s ease forwards" />
|
||||
<path fill="none" stroke="#4caf50" stroke-width="3"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M14 27l7 7 16-16" class="draw"
|
||||
style="stroke-dasharray:50;stroke-dashoffset:50;
|
||||
animation:draw .4s ease .5s forwards" />
|
||||
</svg>
|
||||
```
|
||||
|
||||
### Morphing Hamburger to X
|
||||
|
||||
```svg
|
||||
<svg viewBox="0 0 24 24" id="menu">
|
||||
<path id="top" d="M 3,6 L 21,6" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round">
|
||||
<animate attributeName="d" to="M 5,5 L 19,19" dur="0.3s" begin="menu.click" fill="freeze" />
|
||||
</path>
|
||||
<path id="mid" d="M 3,12 L 21,12" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round">
|
||||
<animate attributeName="opacity" to="0" dur="0.1s" begin="menu.click" fill="freeze" />
|
||||
</path>
|
||||
<path id="bot" d="M 3,18 L 21,18" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round">
|
||||
<animate attributeName="d" to="M 5,19 L 19,5" dur="0.3s" begin="menu.click" fill="freeze" />
|
||||
</path>
|
||||
</svg>
|
||||
```
|
||||
|
||||
### Gradient Animation (Color Shift)
|
||||
|
||||
```svg
|
||||
<defs>
|
||||
<linearGradient id="shift" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%">
|
||||
<animate attributeName="stop-color"
|
||||
values="#e63946;#457b9d;#2a9d8f;#e63946"
|
||||
dur="4s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
<stop offset="100%">
|
||||
<animate attributeName="stop-color"
|
||||
values="#457b9d;#2a9d8f;#e63946;#457b9d"
|
||||
dur="4s" repeatCount="indefinite" />
|
||||
</stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="200" height="100" fill="url(#shift)" rx="8" />
|
||||
```
|
||||
|
||||
### Breathing / Pulsing Glow
|
||||
|
||||
```svg
|
||||
<circle cx="100" cy="100" r="30" fill="#e63946">
|
||||
<animate attributeName="r" values="30;35;30" dur="2s"
|
||||
calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1"
|
||||
repeatCount="indefinite" />
|
||||
<animate attributeName="opacity" values="1;0.6;1" dur="2s"
|
||||
calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1"
|
||||
repeatCount="indefinite" />
|
||||
</circle>
|
||||
```
|
||||
|
||||
### Wave / Liquid Effect
|
||||
|
||||
```svg
|
||||
<path fill="#457b9d" opacity="0.7">
|
||||
<animate attributeName="d" dur="5s" repeatCount="indefinite"
|
||||
values="M 0,40 C 30,35 70,45 100,40 L 100,100 L 0,100 Z;
|
||||
M 0,40 C 30,50 70,30 100,40 L 100,100 L 0,100 Z;
|
||||
M 0,40 C 30,35 70,45 100,40 L 100,100 L 0,100 Z"
|
||||
calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1" />
|
||||
</path>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `viewBox`, never hardcode `width`/`height` in the SVG** — let the container size it. This keeps it resolution-independent.
|
||||
|
||||
2. **`<defs>` for reusable definitions** — gradients, filters, masks, clipPaths, and reusable shapes belong in `<defs>`.
|
||||
|
||||
3. **Prefer SMIL for self-contained SVGs** (icons, logos loaded via `<img>`) — CSS/JS won't work there. Use CSS animations when the SVG is inlined and you want to coordinate with the rest of the page.
|
||||
|
||||
4. **Shape morphing requires matching commands** — same number of path commands, same types, same order. If shapes differ, add invisible intermediate points to equalize.
|
||||
|
||||
5. **`stroke-linecap="round"`** makes line animations look polished.
|
||||
|
||||
6. **`fill="freeze"`** in SMIL keeps the final animation state. Without it, the element snaps back.
|
||||
|
||||
7. **`transform-origin: center`** in CSS — SVG transforms default to the origin (0,0), not the element center. Always set this explicitly.
|
||||
|
||||
8. **Use `getTotalLength()`** in JS to get exact path lengths for stroke animations instead of guessing.
|
||||
|
||||
9. **Layer animations with `<g>` groups** — animate the group transform separately from individual element properties for complex choreography.
|
||||
|
||||
10. **Performance:** SVG animations are GPU-composited when animating `transform` and `opacity`. Animating `d`, `points`, or layout attributes triggers repaints — use sparingly on complex SVGs.
|
||||
|
||||
11. **`will-change: transform`** on animated SVG elements helps the browser optimize compositing.
|
||||
|
||||
12. **Accessibility:** Add `role="img"` and `<title>` / `<desc>` elements. Use `prefers-reduced-motion` media query to disable animations for users who request it:
|
||||
|
||||
```css
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
svg * {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
```
|
||||
26
.gitignore
vendored
|
|
@ -1,3 +1,27 @@
|
|||
# Zola build output (Wrangler deploy source)
|
||||
public/
|
||||
.wrangler/
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Cloudflare
|
||||
.wrangler/
|
||||
.dev.vars
|
||||
|
||||
# Environment / secrets
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# OS junk
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Theme static: only the Vite-built bundle is source-controlled.
|
||||
# Do not copy Zola public/ output here — it overrides the real homepage on build.
|
||||
themes/openfreebooks/static/*
|
||||
!themes/openfreebooks/static/js/
|
||||
themes/openfreebooks/static/js/*
|
||||
!themes/openfreebooks/static/js/bundle.js
|
||||
|
|
|
|||
23
data/contributors.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Man Lung Yeung",
|
||||
"avatar": "contributors/manlung.png",
|
||||
"website": "https://manlung.work",
|
||||
"job_title": "EdTech specialist / software engineer",
|
||||
"education": "BEng in Computer Science (HKUST) + BBA (HKUST)",
|
||||
"contributions": [
|
||||
"Founder",
|
||||
"Math textbook",
|
||||
"Physics textbook",
|
||||
"Chemistry textbook"
|
||||
],
|
||||
"socials": [
|
||||
{ "type": "email", "url": "manlung.yeung@outlook.com" },
|
||||
{ "type": "github", "url": "https://github.com/hananoshikaYomaru" },
|
||||
{ "type": "x", "url": "https://x.com/yomaru_1999" },
|
||||
{ "type": "youtube", "url": "https://www.youtube.com/@yomaru_1999" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
51
data/contributors.types.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Contributor data for Open Free Books (`data/contributors.json`).
|
||||
*
|
||||
* Use `website` for a single personal site. Put email, GitHub, X, and other
|
||||
* profiles in `socials`.
|
||||
*/
|
||||
|
||||
export const CONTRIBUTOR_SOCIAL_TYPES = [
|
||||
"email",
|
||||
"github",
|
||||
"gitlab",
|
||||
"x",
|
||||
"youtube",
|
||||
"bluesky",
|
||||
"linkedin",
|
||||
"mastodon",
|
||||
"instagram",
|
||||
"facebook",
|
||||
"threads",
|
||||
"tiktok",
|
||||
"discord",
|
||||
"orcid",
|
||||
"scholar",
|
||||
"medium",
|
||||
"substack",
|
||||
"rss",
|
||||
] as const;
|
||||
|
||||
export type ContributorSocialType = (typeof CONTRIBUTOR_SOCIAL_TYPES)[number];
|
||||
|
||||
export interface ContributorSocial {
|
||||
type: ContributorSocialType;
|
||||
/** Email address (no `mailto:`) or full https URL for other networks */
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Contributor {
|
||||
id?: string;
|
||||
name: string;
|
||||
avatar?: string;
|
||||
/** Single personal website URL */
|
||||
website?: string;
|
||||
job_title?: string;
|
||||
education?: string;
|
||||
contributions?: string[];
|
||||
socials?: ContributorSocial[];
|
||||
}
|
||||
|
||||
export interface ContributorsFile {
|
||||
contributors: Contributor[];
|
||||
}
|
||||
44
frontend/src/components/contributors-dialog.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { onMount } from "solid-js";
|
||||
|
||||
function openContributorDialog(dialogId: string) {
|
||||
const dialog = document.getElementById(dialogId);
|
||||
if (dialog instanceof HTMLDialogElement) {
|
||||
dialog.showModal();
|
||||
}
|
||||
}
|
||||
|
||||
function wireContributorDialogs() {
|
||||
const buttons = document.querySelectorAll<HTMLButtonElement>("[data-contributor-dialog]");
|
||||
const dialogs = document.querySelectorAll<HTMLDialogElement>(".contributor-dialog");
|
||||
|
||||
const onButtonClick = (event: Event) => {
|
||||
const button = event.currentTarget;
|
||||
if (!(button instanceof HTMLButtonElement)) return;
|
||||
const dialogId = button.dataset.contributorDialog;
|
||||
if (dialogId) openContributorDialog(dialogId);
|
||||
};
|
||||
|
||||
const onDialogClick = (event: MouseEvent) => {
|
||||
const dialog = event.currentTarget;
|
||||
if (!(dialog instanceof HTMLDialogElement)) return;
|
||||
if (event.target === dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
};
|
||||
|
||||
buttons.forEach((button) => button.addEventListener("click", onButtonClick));
|
||||
dialogs.forEach((dialog) => dialog.addEventListener("click", onDialogClick));
|
||||
|
||||
return () => {
|
||||
buttons.forEach((button) => button.removeEventListener("click", onButtonClick));
|
||||
dialogs.forEach((dialog) => dialog.removeEventListener("click", onDialogClick));
|
||||
};
|
||||
}
|
||||
|
||||
export function ContributorsDialogBootstrap() {
|
||||
onMount(() => {
|
||||
return wireContributorDialogs();
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1,5 +1,35 @@
|
|||
import { onCleanup, onMount } from "solid-js";
|
||||
|
||||
const STAGGER_MS = 80;
|
||||
|
||||
function getStaggerGroup(card: HTMLElement): HTMLElement {
|
||||
return (
|
||||
card.closest("[data-scroll-reveal]") ??
|
||||
card.closest("section") ??
|
||||
card.parentElement ??
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
||||
function isInViewport(el: HTMLElement): boolean {
|
||||
const rect = el.getBoundingClientRect();
|
||||
return rect.top < window.innerHeight && rect.bottom > 0;
|
||||
}
|
||||
|
||||
function groupCards(cards: HTMLElement[]): Map<HTMLElement, HTMLElement[]> {
|
||||
const groups = new Map<HTMLElement, HTMLElement[]>();
|
||||
for (const card of cards) {
|
||||
const group = getStaggerGroup(card);
|
||||
const list = groups.get(group);
|
||||
if (list) {
|
||||
list.push(card);
|
||||
} else {
|
||||
groups.set(group, [card]);
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
export function ScrollRevealBootstrap() {
|
||||
onMount(() => {
|
||||
const cards = document.querySelectorAll<HTMLElement>("[data-scroll-reveal-card]");
|
||||
|
|
@ -23,10 +53,20 @@ export function ScrollRevealBootstrap() {
|
|||
{ threshold: 0.14 }
|
||||
);
|
||||
|
||||
cards.forEach((card, index) => {
|
||||
card.style.transitionDelay = `${index * 80}ms`;
|
||||
observer.observe(card);
|
||||
});
|
||||
const groups = groupCards([...cards]);
|
||||
|
||||
for (const groupCards of groups.values()) {
|
||||
groupCards.forEach((card, index) => {
|
||||
if (isInViewport(card)) {
|
||||
card.style.transitionDelay = "0ms";
|
||||
card.classList.add("is-inview");
|
||||
return;
|
||||
}
|
||||
|
||||
card.style.transitionDelay = `${index * STAGGER_MS}ms`;
|
||||
observer.observe(card);
|
||||
});
|
||||
}
|
||||
|
||||
onCleanup(() => observer.disconnect());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { render } from "solid-js/web";
|
||||
import { SiteHeader } from "./components/site-header";
|
||||
import { Marquee } from "./components/marquee";
|
||||
import { ContributorsDialogBootstrap } from "./components/contributors-dialog";
|
||||
import { ScrollRevealBootstrap } from "./components/scroll-reveal";
|
||||
import { initTheme } from "./theme";
|
||||
|
||||
|
|
@ -56,4 +57,5 @@ if (marqueeRoot) {
|
|||
}
|
||||
|
||||
render(() => <ScrollRevealBootstrap />, document.body);
|
||||
render(() => <ContributorsDialogBootstrap />, document.body);
|
||||
mountFooterYear();
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@
|
|||
"sourceType": "github",
|
||||
"skillPath": "microinteractions/SKILL.md",
|
||||
"computedHash": "875eca02a058bca5467850587d6c7aed62f3de1cd9f509f52c95253ec267b518"
|
||||
},
|
||||
"svg-animations": {
|
||||
"source": "supermemoryai/skills",
|
||||
"sourceType": "github",
|
||||
"skillPath": "svg-animations/SKILL.md",
|
||||
"computedHash": "9be4b3e0afe9d3cbb1bcf238f8504f9a23552f742350fbef9483070112ac1977"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
static/contributors.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Man Lung Yeung",
|
||||
"avatar": "contributors/manlung.png",
|
||||
"website": "https://manlung.work",
|
||||
"job_title": "EdTech specialist / software engineer",
|
||||
"education": "BEng in Computer Science (HKUST) + BBA (HKUST)",
|
||||
"contributions": [
|
||||
"Project Creator",
|
||||
"Math textbook",
|
||||
"Physics textbook",
|
||||
"Chemistry textbook"
|
||||
],
|
||||
"socials": [
|
||||
{ "type": "email", "url": "manlung.yeung@outlook.com" },
|
||||
{ "type": "github", "url": "https://github.com/hananoshikaYomaru" },
|
||||
{ "type": "x", "url": "https://x.com/yomaru_1999" },
|
||||
{ "type": "youtube", "url": "https://www.youtube.com/@yomaru_1999" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
static/contributors/manlung.png
Normal file
|
After Width: | Height: | Size: 335 KiB |
303
themes/openfreebooks/sass/_contributors.scss
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
.why-section--community {
|
||||
--community-block-gap: 72px;
|
||||
}
|
||||
|
||||
.why-section--community .why-craft-logos-wrap {
|
||||
margin-top: var(--community-block-gap);
|
||||
}
|
||||
|
||||
.why-section--community .community-contributors {
|
||||
margin-top: var(--community-block-gap);
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.contributors-intro {
|
||||
max-width: var(--prose-width);
|
||||
margin: 0 0 24px;
|
||||
color: var(--soft-ink);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.72;
|
||||
}
|
||||
|
||||
.contributors-avatar-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.contributors-avatar-item {
|
||||
opacity: 0;
|
||||
translate: 0 12px;
|
||||
transition: opacity 400ms ease, translate 400ms ease;
|
||||
}
|
||||
|
||||
.contributors-avatar-item.is-inview {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
.contributor-avatar-btn {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 50%;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
line-height: 0;
|
||||
transition: border-color 160ms ease, transform 160ms ease;
|
||||
}
|
||||
|
||||
.contributor-avatar-btn:hover,
|
||||
.contributor-avatar-btn:focus-visible {
|
||||
border-color: var(--accent);
|
||||
transform: translateY(-2px);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.contributor-avatar-img,
|
||||
.contributor-avatar-fallback {
|
||||
display: block;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.contributor-avatar-img {
|
||||
object-fit: cover;
|
||||
border: 1px solid var(--card-border);
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.contributor-avatar-fallback {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
color: var(--accent);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.contributor-dialog {
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
border: none;
|
||||
max-width: min(92vw, 440px);
|
||||
background-color: #f5f4f1;
|
||||
color: var(--ink);
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .contributor-dialog {
|
||||
background-color: #1a1917;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) .contributor-dialog {
|
||||
background-color: #1a1917;
|
||||
}
|
||||
}
|
||||
|
||||
.contributor-dialog::backdrop {
|
||||
background: rgba(26, 26, 26, 0.45);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .contributor-dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.62);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) .contributor-dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.62);
|
||||
}
|
||||
}
|
||||
|
||||
.contributor-dialog-panel {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 28px 28px 24px;
|
||||
border: 1px solid var(--card-border);
|
||||
background-color: #f5f4f1;
|
||||
box-shadow: 0 18px 48px var(--shadow-soft);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .contributor-dialog-panel {
|
||||
background-color: #1a1917;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme="light"]) .contributor-dialog-panel {
|
||||
background-color: #1a1917;
|
||||
}
|
||||
}
|
||||
|
||||
.contributor-dialog-close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contributor-dialog-close:hover,
|
||||
.contributor-dialog-close:focus-visible {
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.contributor-dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
padding-right: 28px;
|
||||
}
|
||||
|
||||
.contributor-dialog-avatar {
|
||||
flex-shrink: 0;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 1px solid var(--card-border);
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.contributor-dialog-name {
|
||||
margin: 0 0 6px;
|
||||
font-family: var(--font-heading);
|
||||
font-size: clamp(1.35rem, 2vw, 1.75rem);
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.contributor-dialog-job {
|
||||
margin: 0;
|
||||
color: var(--accent);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.contributor-dialog-meta,
|
||||
.contributor-dialog-contributions {
|
||||
margin: 0 0 14px;
|
||||
color: var(--soft-ink);
|
||||
font-size: 0.98rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.contributor-meta-label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.contributor-contribution-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.contributor-contribution-list li {
|
||||
position: relative;
|
||||
padding-left: 1rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.contributor-contribution-list li::before {
|
||||
content: "—";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.contributor-dialog-connect {
|
||||
margin-top: 18px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.contributor-social-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 8px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.contributor-social-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 50%;
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
background: var(--paper);
|
||||
transition: border-color 160ms ease, color 160ms ease, transform 160ms ease;
|
||||
}
|
||||
|
||||
.contributor-social-link:hover,
|
||||
.contributor-social-link:focus-visible {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
transform: translateY(-1px);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.contributor-social-icon {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.why-section--community .section-note a {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.why-section--community .community-contributors .section-note {
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.contributors-avatar-item {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.contributor-avatar-btn:hover,
|
||||
.contributor-avatar-btn:focus-visible {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,12 @@ body::before {
|
|||
background-size: 180px 180px;
|
||||
}
|
||||
|
||||
/* Top-layer modals must stay opaque; grain blend would otherwise show through. */
|
||||
.contributor-dialog[open],
|
||||
.contributor-dialog[open] .contributor-dialog-panel {
|
||||
mix-blend-mode: normal;
|
||||
}
|
||||
|
||||
[data-theme="dark"] body::before {
|
||||
opacity: 0.45;
|
||||
mix-blend-mode: soft-light;
|
||||
|
|
|
|||
267
themes/openfreebooks/sass/_why-sections.scss
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
.why-section {
|
||||
padding: 64px 0;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.why-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: clamp(32px, 5vw, 56px);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.why-row--art-end .why-art {
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.why-row--art-end .why-copy {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.why-art {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
translate: 0 20px;
|
||||
transition: opacity 500ms ease, translate 500ms ease;
|
||||
}
|
||||
|
||||
.why-art.is-inview {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
.why-art svg {
|
||||
width: min(100%, 280px);
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.why-copy {
|
||||
max-width: var(--prose-width);
|
||||
opacity: 0;
|
||||
translate: 0 20px;
|
||||
transition: opacity 500ms ease, translate 500ms ease;
|
||||
}
|
||||
|
||||
.why-copy.is-inview {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
.why-copy .section-title {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.why-copy p {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--soft-ink);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.72;
|
||||
}
|
||||
|
||||
.why-copy p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.why-copy strong {
|
||||
color: var(--ink);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.why-section--community .why-craft-logos-wrap {
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
translate: 0 20px;
|
||||
transition: opacity 500ms ease, translate 500ms ease;
|
||||
}
|
||||
|
||||
.why-section--community .why-craft-logos-wrap.is-inview {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
}
|
||||
|
||||
.university-logos-label {
|
||||
margin: 0 0 24px;
|
||||
text-align: center;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.university-carousel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
width: 100vw;
|
||||
margin-left: calc(-50vw + 50%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.university-carousel-row {
|
||||
overflow: hidden;
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
#000 6%,
|
||||
#000 94%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.university-carousel-track {
|
||||
display: flex;
|
||||
width: max-content;
|
||||
animation: university-carousel-forward 52s linear infinite;
|
||||
}
|
||||
|
||||
.university-carousel-row--reverse .university-carousel-track {
|
||||
animation-name: university-carousel-reverse;
|
||||
animation-duration: 58s;
|
||||
}
|
||||
|
||||
.university-carousel-row--slow .university-carousel-track {
|
||||
animation-duration: 64s;
|
||||
}
|
||||
|
||||
.university-carousel-group {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@keyframes university-carousel-forward {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes university-carousel-reverse {
|
||||
from {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.university-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 56px;
|
||||
padding: 10px 12px;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
}
|
||||
|
||||
.university-logo svg {
|
||||
width: 108px;
|
||||
height: auto;
|
||||
display: block;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
$university-colors: (
|
||||
mit: (#a31f34, #e85a6f),
|
||||
harvard: (#a51c30, #e88a96),
|
||||
stanford: (#8c1515, #e88a8a),
|
||||
berkeley: (#003262, #6a9fd4),
|
||||
caltech: (#ff6c0c, #f5a623),
|
||||
princeton: (#e77500, #f0a040),
|
||||
yale: (#00356b, #6a9fd4),
|
||||
columbia: (#003865, #7eb3e8),
|
||||
cornell: (#b31b1b, #e85a6f),
|
||||
cmu: (#c41230, #e87888),
|
||||
oxford: (#002147, #7eb3e8),
|
||||
cambridge: (#0072cf, #6ab0f0),
|
||||
imperial: (#003e74, #6a9fd4),
|
||||
lse: (#e41f13, #f07870),
|
||||
eth: (#1f407a, #6a9fd4),
|
||||
toronto: (#002a5c, #6a9fd4),
|
||||
mcgill: (#ed1b2f, #f07888),
|
||||
hku: (#006747, #3cb88a),
|
||||
hkust: (#003366, #6a9fd4),
|
||||
cuhk: (#7b2d8e, #c49ae0),
|
||||
nus: (#ef7c00, #f5a623),
|
||||
tsinghua: (#82318e, #c49ae0),
|
||||
pku: (#94070a, #e87878),
|
||||
utokyo: (#003f87, #6a9fd4),
|
||||
anu: (#c4a337, #e8c860),
|
||||
melbourne: (#094183, #6a9fd4),
|
||||
iitb: (#003366, #6a9fd4)
|
||||
);
|
||||
|
||||
@each $slug, $pair in $university-colors {
|
||||
.university-logo--#{$slug} {
|
||||
color: nth($pair, 1);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .university-logo--#{$slug} {
|
||||
color: nth($pair, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@each $slug, $pair in $university-colors {
|
||||
:root:not([data-theme="light"]) .university-logo--#{$slug} {
|
||||
color: nth($pair, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.why-row,
|
||||
.why-row--art-end {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.why-row--art-end .why-art,
|
||||
.why-row--art-end .why-copy {
|
||||
order: unset;
|
||||
}
|
||||
|
||||
.why-art svg {
|
||||
width: min(100%, 220px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.why-art,
|
||||
.why-copy,
|
||||
.why-section--community .why-craft-logos-wrap {
|
||||
opacity: 1;
|
||||
translate: 0 0;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.university-carousel-track {
|
||||
animation: none;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.university-carousel-row {
|
||||
mask-image: none;
|
||||
}
|
||||
|
||||
.university-carousel-group[aria-hidden="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.university-carousel {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,3 +2,5 @@
|
|||
@import "grain";
|
||||
@import "layout";
|
||||
@import "components";
|
||||
@import "why-sections";
|
||||
@import "contributors";
|
||||
|
|
|
|||
|
|
@ -58,6 +58,538 @@
|
|||
|
||||
<div id="marquee"></div>
|
||||
|
||||
<section class="why-section" id="why-textbook-costs">
|
||||
<div class="container why-row">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Stack of textbooks and cost</title>
|
||||
<rect x="48" y="118" width="104" height="12" rx="2" fill="var(--accent)" opacity="0.25"/>
|
||||
<path d="M52 118 L52 72 L88 64 L88 118 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M88 118 L88 64 L124 72 L124 118 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M124 118 L124 76 L160 84 L160 118 Z" fill="var(--accent)" fill-opacity="0.12" stroke="var(--accent)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M56 72 L88 64 L88 68 L56 76 Z" fill="var(--accent)" fill-opacity="0.35" stroke="none"/>
|
||||
<circle cx="148" cy="52" r="28" fill="none" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<text x="148" y="58" text-anchor="middle" font-family="var(--mono)" font-size="18" fill="var(--accent)">$</text>
|
||||
<path d="M36 148 L164 148" stroke="var(--ink)" stroke-width="1" stroke-dasharray="4 4" opacity="0.4"/>
|
||||
<path d="M72 40 L72 28 M68 34 L76 34" stroke="var(--soft-ink)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">The problem</p>
|
||||
<h2 class="section-title">Textbook fees hit families hard</h2>
|
||||
<p>
|
||||
In many countries, families pay hundreds of dollars per child each year for printed textbooks —
|
||||
on top of tuition, devices, and supplies. When money is tight, students share worn copies, skip
|
||||
buying books, or fall behind before the term begins.
|
||||
</p>
|
||||
<p>
|
||||
That cost is not a small inconvenience. It is a barrier to showing up prepared. Open Free Books
|
||||
exists so <strong>every learner can start the year with complete materials</strong>, not a receipt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-html">
|
||||
<div class="container why-row why-row--art-end">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Paper and PDF versus interactive HTML</title>
|
||||
<rect x="24" y="48" width="56" height="72" rx="2" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M32 60 L72 60 M32 72 L68 72 M32 84 L64 84" stroke="var(--soft-ink)" stroke-width="1" stroke-linecap="round"/>
|
||||
<text x="52" y="108" text-anchor="middle" font-family="var(--mono)" font-size="9" fill="var(--muted)">PDF</text>
|
||||
<rect x="88" y="48" width="56" height="72" rx="2" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-dasharray="3 3" opacity="0.5"/>
|
||||
<path d="M96 60 L136 60 M96 72 L132 72" stroke="var(--soft-ink)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
<path d="M148 56 L148 160" stroke="var(--accent)" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M140 72 L156 72 L152 88 L144 88 Z" fill="var(--accent)" fill-opacity="0.2" stroke="var(--accent)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<rect x="156" y="56" width="36" height="104" rx="4" fill="var(--card-bg)" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<path d="M164 72 L184 72 M164 84 L180 84 M164 96 L182 96" stroke="var(--accent)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<circle cx="174" cy="118" r="8" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M171 118 L173 120 L177 114" fill="none" stroke="var(--accent)" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="174" y="140" text-anchor="middle" font-family="var(--mono)" font-size="8" fill="var(--accent)"></></text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">The medium</p>
|
||||
<h2 class="section-title">Long live the web — not the PDF</h2>
|
||||
<p>
|
||||
Paper textbooks are fixed: one layout, one edition, no room to fix a typo until the next print run.
|
||||
Most “digital” versions are the same pages exported to PDF — still static, still hard to search,
|
||||
still impossible to embed a simulation or a live diagram.
|
||||
</p>
|
||||
<p>
|
||||
<strong>HTML on the open web</strong> is different. Chapters can link to sources, resize for any screen,
|
||||
include interactive figures, and improve over time. The book becomes something you use — not something
|
||||
you only carry.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-teachers">
|
||||
<div class="container why-row">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Teacher guiding, open textbook</title>
|
||||
<circle cx="72" cy="56" r="18" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M48 108 C48 88 60 78 72 78 C84 78 96 88 96 108 L96 130 L48 130 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M56 98 L88 98" stroke="var(--accent)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<path d="M108 72 L108 148 L168 148 L168 88 L148 72 Z" fill="var(--accent)" fill-opacity="0.1" stroke="var(--accent)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M116 92 L160 92 M116 108 L152 108 M116 124 L156 124" stroke="var(--ink)" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
|
||||
<path d="M72 130 L72 160 M60 160 L84 160" stroke="var(--ink)" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M100 56 L132 72" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="2 3"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">People & books</p>
|
||||
<h2 class="section-title">Replace textbooks, not teachers</h2>
|
||||
<p>
|
||||
A great teacher — whether in person or, someday, assisted by thoughtful tools — still sets pace,
|
||||
asks hard questions, and notices when a student is stuck. That relationship is not what we are
|
||||
trying to automate.
|
||||
</p>
|
||||
<p>
|
||||
Textbooks are different. They are reference and structure: explanations, exercises, diagrams.
|
||||
Those can be <strong>free, forkable, and kept current on the web</strong> so teachers spend less
|
||||
time hunting permissions and more time teaching.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-learning">
|
||||
<div class="container why-row why-row--art-end">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Student thinking with a textbook</title>
|
||||
<circle cx="100" cy="52" r="20" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M68 108 C68 86 82 74 100 74 C118 74 132 86 132 108 L132 132 L68 132 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<rect x="88" y="136" width="24" height="32" rx="2" fill="var(--accent)" fill-opacity="0.15" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<path d="M94 148 L106 148 M94 156 L106 156" stroke="var(--accent)" stroke-width="1" stroke-linecap="round"/>
|
||||
<path d="M44 64 Q28 48 36 32" fill="none" stroke="var(--soft-ink)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<path d="M40 36 L36 28 M44 34 L48 26" stroke="var(--soft-ink)" stroke-width="1" stroke-linecap="round"/>
|
||||
<text x="32" y="56" font-family="var(--font-heading)" font-size="14" fill="var(--accent)" font-style="italic">?</text>
|
||||
<path d="M156 72 L168 60 M160 88 L176 88" stroke="var(--muted)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">Agency</p>
|
||||
<h2 class="section-title">Do not outsource your learning</h2>
|
||||
<p>
|
||||
Finishing a chapter is not the same as understanding it. Skimming for answers, copying worked
|
||||
solutions, or rushing to the end leaves gaps that show up on the next test — and in the years after.
|
||||
</p>
|
||||
<p>
|
||||
Read slowly. Try the problems before you peek at hints. Sit with confusion long enough to name what
|
||||
you do not get. <strong>The textbook is a guide; the thinking still has to be yours.</strong> That habit
|
||||
is what school is really training, whether the subject is algebra or history.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-not-ai">
|
||||
<div class="container why-row">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Open textbook and the earth</title>
|
||||
<circle cx="100" cy="100" r="44" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M56 100 C56 72 76 56 100 56 C124 56 144 72 144 100 C144 128 124 144 100 144 C76 144 56 128 56 100 Z" fill="var(--accent)" fill-opacity="0.08" stroke="none"/>
|
||||
<path d="M56 92 C80 88 88 108 100 108 C112 108 120 88 144 92" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M64 112 C88 118 96 98 108 98 C120 98 128 118 136 112" fill="none" stroke="var(--soft-ink)" stroke-width="1"/>
|
||||
<rect x="128" y="36" width="48" height="40" rx="6" fill="none" stroke="var(--muted)" stroke-width="1.2" stroke-dasharray="4 3" opacity="0.55"/>
|
||||
<path d="M136 52 L168 52 M136 60 L160 60" stroke="var(--muted)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
<path d="M120 36 L128 28" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<rect x="72" y="118" width="56" height="40" rx="2" fill="var(--card-bg)" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<path d="M80 132 L120 132 M80 142 L112 142" stroke="var(--ink)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
<text x="100" y="154" text-anchor="middle" font-family="var(--mono)" font-size="7" fill="var(--accent)">OPEN</text>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">Planet</p>
|
||||
<h2 class="section-title">Open textbooks are gentler on the Earth</h2>
|
||||
<p>
|
||||
Core curriculum changes slowly. Newton’s laws, the structure of a cell, and how fractions work do not
|
||||
need a new AI run every September. Regenerating the same chapter thousands of times burns electricity
|
||||
in data centers — for text that could have been written once and shared openly.
|
||||
</p>
|
||||
<p>
|
||||
<strong>One carefully reviewed book</strong>, published on the web for everyone, means less redundant
|
||||
compute, less e-waste from disposable PDFs, and fewer trucks moving heavy print runs. Students get
|
||||
stable material they can trust; the environment gets less waste from doing the same work over and over.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section why-section--craft" id="why-craft">
|
||||
<div class="container why-row why-row--art-end">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Scholars and institutions worldwide</title>
|
||||
<circle cx="100" cy="92" r="46" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M54 92 C54 68 74 52 100 52 C126 52 146 68 146 92" fill="var(--accent)" fill-opacity="0.08" stroke="none"/>
|
||||
<path d="M54 92 C72 108 88 112 100 112 C112 112 128 108 146 92" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M54 92 C68 76 84 68 100 68 C116 68 132 76 146 92" fill="none" stroke="var(--soft-ink)" stroke-width="1" opacity="0.5"/>
|
||||
<circle cx="72" cy="148" r="10" fill="none" stroke="var(--ink)" stroke-width="1.2"/>
|
||||
<path d="M66 158 C66 150 69 146 72 146 C75 146 78 150 78 158 L78 168 L66 168 Z" fill="none" stroke="var(--ink)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<circle cx="100" cy="152" r="10" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M94 162 C94 154 97 150 100 150 C103 150 106 154 106 162 L106 172 L94 172 Z" fill="var(--accent)" fill-opacity="0.12" stroke="var(--accent)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<circle cx="128" cy="148" r="10" fill="none" stroke="var(--ink)" stroke-width="1.2"/>
|
||||
<path d="M122 158 C122 150 125 146 128 146 C131 146 134 150 134 158 L134 168 L122 168 Z" fill="none" stroke="var(--ink)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path d="M82 48 L88 36 M100 44 L100 32 M118 48 L112 36" stroke="var(--accent)" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">Craft</p>
|
||||
<h2 class="section-title">Carefully crafted by brilliant people on Earth</h2>
|
||||
<p>
|
||||
A textbook is not a bulk download. It is years of lectures distilled, diagrams redrawn until they
|
||||
click, exercises tested in real classrooms, and arguments tightened by reviewers who care about
|
||||
getting it right.
|
||||
</p>
|
||||
<p>
|
||||
Open Free Books gathers that work from educators and researchers at institutions around the world —
|
||||
people who have already spent their careers making hard ideas legible. We publish their care
|
||||
<strong>openly</strong> so the next student inherits the best version, not a disposable draft.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container why-craft-logos-wrap" data-scroll-reveal-card>
|
||||
<p class="university-logos-label">Contributors and partners from leading universities include</p>
|
||||
<div class="university-carousel" role="region" aria-label="Universities represented among contributors">
|
||||
<div class="university-carousel-row university-carousel-row--forward">
|
||||
<div class="university-carousel-track">
|
||||
<ul class="university-carousel-group" role="list">
|
||||
<li class="university-logo university-logo--mit">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="MIT">
|
||||
<text x="60" y="34" text-anchor="middle" font-family="var(--mono)" font-size="22" font-weight="700" letter-spacing="0.12em" fill="currentColor">MIT</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--harvard">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Harvard University">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Harvard</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="8" letter-spacing="0.2em" fill="currentColor" opacity="0.75">UNIVERSITY</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--stanford">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Stanford University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="17" fill="currentColor">Stanford</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--berkeley">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="UC Berkeley">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="16" font-weight="700" fill="currentColor">Berkeley</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF CALIFORNIA</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--caltech">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Caltech">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">Caltech</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--princeton">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Princeton University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">Princeton</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--yale">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Yale University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="17" fill="currentColor">Yale</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--columbia">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Columbia University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Columbia</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cornell">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Cornell University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">Cornell</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<ul class="university-carousel-group" role="list" aria-hidden="true">
|
||||
<li class="university-logo university-logo--mit">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="MIT">
|
||||
<text x="60" y="34" text-anchor="middle" font-family="var(--mono)" font-size="22" font-weight="700" letter-spacing="0.12em" fill="currentColor">MIT</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--harvard">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Harvard University">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Harvard</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="8" letter-spacing="0.2em" fill="currentColor" opacity="0.75">UNIVERSITY</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--stanford">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Stanford University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="17" fill="currentColor">Stanford</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--berkeley">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="UC Berkeley">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="16" font-weight="700" fill="currentColor">Berkeley</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF CALIFORNIA</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--caltech">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Caltech">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">Caltech</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--princeton">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Princeton University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">Princeton</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--yale">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Yale University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="17" fill="currentColor">Yale</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--columbia">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Columbia University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Columbia</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cornell">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Cornell University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">Cornell</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="university-carousel-row university-carousel-row--reverse">
|
||||
<div class="university-carousel-track">
|
||||
<ul class="university-carousel-group" role="list">
|
||||
<li class="university-logo university-logo--cmu">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Carnegie Mellon University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">CMU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--oxford">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Oxford">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="16" font-style="italic" fill="currentColor">Oxford</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.14em" fill="currentColor" opacity="0.7">UNIVERSITY</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cambridge">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Cambridge">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Cambridge</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--imperial">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Imperial College London">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="13" font-weight="700" fill="currentColor">Imperial</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">COLLEGE LONDON</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--lse">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="London School of Economics">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">LSE</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--eth">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="ETH Zurich">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">ETH</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--toronto">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Toronto">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Toronto</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--mcgill">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="McGill University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">McGill</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--hku">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The University of Hong Kong">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">HKU</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">THE UNIVERSITY OF HONG KONG</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<ul class="university-carousel-group" role="list" aria-hidden="true">
|
||||
<li class="university-logo university-logo--cmu">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Carnegie Mellon University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">CMU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--oxford">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Oxford">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="16" font-style="italic" fill="currentColor">Oxford</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.14em" fill="currentColor" opacity="0.7">UNIVERSITY</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cambridge">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Cambridge">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Cambridge</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--imperial">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Imperial College London">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="13" font-weight="700" fill="currentColor">Imperial</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">COLLEGE LONDON</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--lse">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="London School of Economics">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">LSE</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--eth">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="ETH Zurich">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">ETH</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--toronto">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Toronto">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Toronto</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--mcgill">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="McGill University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">McGill</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--hku">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The University of Hong Kong">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">HKU</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">THE UNIVERSITY OF HONG KONG</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="university-carousel-row university-carousel-row--forward university-carousel-row--slow">
|
||||
<div class="university-carousel-track">
|
||||
<ul class="university-carousel-group" role="list">
|
||||
<li class="university-logo university-logo--hkust">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Hong Kong University of Science and Technology">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="17" font-weight="700" letter-spacing="0.06em" fill="currentColor">HKUST</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cuhk">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The Chinese University of Hong Kong">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">CUHK</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--nus">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="National University of Singapore">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">NUS</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.06em" fill="currentColor" opacity="0.7">NATIONAL UNIVERSITY OF SINGAPORE</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--tsinghua">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Tsinghua University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Tsinghua</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--pku">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Peking University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">PKU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--utokyo">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The University of Tokyo">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="14" fill="currentColor">Tokyo</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--anu">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Australian National University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">ANU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--melbourne">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Melbourne">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Melbourne</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--iitb">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Indian Institute of Technology Bombay">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">IIT</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">BOMBAY</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<ul class="university-carousel-group" role="list" aria-hidden="true">
|
||||
<li class="university-logo university-logo--hkust">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Hong Kong University of Science and Technology">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="17" font-weight="700" letter-spacing="0.06em" fill="currentColor">HKUST</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cuhk">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The Chinese University of Hong Kong">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">CUHK</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--nus">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="National University of Singapore">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">NUS</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.06em" fill="currentColor" opacity="0.7">NATIONAL UNIVERSITY OF SINGAPORE</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--tsinghua">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Tsinghua University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Tsinghua</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--pku">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Peking University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">PKU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--utokyo">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The University of Tokyo">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="14" fill="currentColor">Tokyo</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--anu">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Australian National University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">ANU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--melbourne">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Melbourne">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Melbourne</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--iitb">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Indian Institute of Technology Bombay">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">IIT</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">BOMBAY</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="browse-by-subject">
|
||||
<div class="container">
|
||||
<p class="section-kicker">Catalog</p>
|
||||
|
|
@ -108,6 +640,106 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<section id="contributors" class="contributors-section">
|
||||
<div class="container">
|
||||
<p class="section-kicker">People</p>
|
||||
<h2 class="section-title">Contributors</h2>
|
||||
<p class="contributors-intro">
|
||||
Open Free Books is written and maintained in the open. These are the people building and reviewing the textbooks.
|
||||
</p>
|
||||
<ul class="contributors-avatar-grid" role="list">
|
||||
|
||||
|
||||
<li class="contributors-avatar-item" data-scroll-reveal-card>
|
||||
<button
|
||||
type="button"
|
||||
class="contributor-avatar-btn"
|
||||
data-contributor-dialog="contributor-dialog-man-lung-yeung"
|
||||
aria-label="View contributions by Man Lung Yeung"
|
||||
aria-haspopup="dialog"
|
||||
>
|
||||
|
||||
<img
|
||||
class="contributor-avatar-img"
|
||||
src="https://openfreebooks.org/contributors/manlung.png"
|
||||
alt=""
|
||||
width="48"
|
||||
height="48"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
</button>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<dialog id="contributor-dialog-man-lung-yeung" class="contributor-dialog">
|
||||
<form method="dialog" class="contributor-dialog-panel">
|
||||
<button type="submit" class="contributor-dialog-close" aria-label="Close">×</button>
|
||||
<div class="contributor-dialog-header">
|
||||
|
||||
<img
|
||||
class="contributor-dialog-avatar"
|
||||
src="https://openfreebooks.org/contributors/manlung.png"
|
||||
alt=""
|
||||
width="80"
|
||||
height="80"
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
<div class="contributor-dialog-identity">
|
||||
<h3 class="contributor-dialog-name">Man Lung Yeung</h3>
|
||||
|
||||
<p class="contributor-dialog-job">EdTech specialist / software engineer</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="contributor-dialog-meta">
|
||||
<span class="contributor-meta-label">Education</span>
|
||||
BEng in Computer Science (HKUST) + BBA (HKUST)
|
||||
</p>
|
||||
|
||||
|
||||
<div class="contributor-dialog-contributions">
|
||||
<span class="contributor-meta-label">Contributes</span>
|
||||
<ul class="contributor-contribution-list" role="list">
|
||||
|
||||
<li>Founder</li>
|
||||
|
||||
<li>Math textbook</li>
|
||||
|
||||
<li>Physics textbook</li>
|
||||
|
||||
<li>Chemistry textbook</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="contributor-dialog-links">
|
||||
|
||||
<a class="contributor-link" href="mailto:manlung.yeung@outlook.com">manlung.yeung@outlook.com</a>
|
||||
|
||||
|
||||
<a class="contributor-link contributor-link--external" href="https://manlung.work" rel="noopener noreferrer">Website</a>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
|
||||
<p class="section-note">
|
||||
Want to contribute? Open a pull request on <a href="https://github.com/hananoshikayomaru/openfreebooks">GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
{% import "macros/social-icons.html" as social %}
|
||||
|
||||
{% block title %}{{ section.title }}{% endblock title %}
|
||||
{% block meta_description %}
|
||||
|
|
@ -27,6 +28,10 @@
|
|||
|
||||
<div id="marquee"></div>
|
||||
|
||||
{% include "partials/why-sections.html" %}
|
||||
|
||||
{% include "partials/community-section.html" %}
|
||||
|
||||
<section id="browse-by-subject">
|
||||
<div class="container">
|
||||
<p class="section-kicker">Catalog</p>
|
||||
|
|
|
|||
91
themes/openfreebooks/templates/macros/social-icons.html
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
{% macro social_label(kind) %}
|
||||
{% if kind == "email" %}Email
|
||||
{% elif kind == "github" %}GitHub
|
||||
{% elif kind == "gitlab" %}GitLab
|
||||
{% elif kind == "x" %}X
|
||||
{% elif kind == "youtube" %}YouTube
|
||||
{% elif kind == "bluesky" %}Bluesky
|
||||
{% elif kind == "linkedin" %}LinkedIn
|
||||
{% elif kind == "mastodon" %}Mastodon
|
||||
{% elif kind == "instagram" %}Instagram
|
||||
{% elif kind == "facebook" %}Facebook
|
||||
{% elif kind == "threads" %}Threads
|
||||
{% elif kind == "tiktok" %}TikTok
|
||||
{% elif kind == "discord" %}Discord
|
||||
{% elif kind == "orcid" %}ORCID
|
||||
{% elif kind == "scholar" %}Google Scholar
|
||||
{% elif kind == "medium" %}Medium
|
||||
{% elif kind == "substack" %}Substack
|
||||
{% elif kind == "rss" %}RSS
|
||||
{% else %}{{ kind | capitalize }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro social_href(social) %}
|
||||
{% if social.type == "email" %}mailto:{{ social.url | safe }}{% else %}{{ social.url | safe }}{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro social_icon(kind) %}
|
||||
<svg class="contributor-social-icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
{% if kind == "email" %}
|
||||
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
||||
<path d="M3 7l9 6 9-6"/>
|
||||
{% elif kind == "github" %}
|
||||
<path d="M9 19c-4.3 1.4-4.3-2.5-6-3m12 5v-3.5c0-1 .1-1.4-.3-2 2.4-.3 5-1.2 5-5.4.9-1.5-.3-3.2 0-4.8-.9-.3-2.9.4-4.5 0-1.3-.4-2.7-.4-4 0-1.6-.4-3.6.3-4.5 0 .3 1.6.3 3.6 0 4.8 0 4.2 2.6 5.1 5 5.4-.4 1-.4 1.3-.4 2.3V20" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "gitlab" %}
|
||||
<path d="M4 20l4-12 4 8 4-8 4 12H4z" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "x" %}
|
||||
<path d="M4 4l7.5 8.5L4 20h2.5l5-5.7L15.5 20H20l-7.8-9L20 4h-2.5l-4.6 5.2L8.5 4H4z" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "youtube" %}
|
||||
<rect x="3" y="6" width="18" height="12" rx="3"/>
|
||||
<path d="M11 10l5 2-5 2v-4z" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "bluesky" %}
|
||||
<path d="M6.5 5.5c3.2 2.4 6.8 2.4 10 0 0 5.6-2.5 11-5 11s-5-5.4-5-11z" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "linkedin" %}
|
||||
<rect x="4" y="4" width="4" height="16"/>
|
||||
<rect x="10" y="10" width="4" height="10"/>
|
||||
<circle cx="6" cy="7" r="2" fill="currentColor" stroke="none"/>
|
||||
<path d="M14 8c2.2 0 4 1.8 4 4v8"/>
|
||||
{% elif kind == "mastodon" %}
|
||||
<rect x="4" y="6" width="16" height="12" rx="3"/>
|
||||
<path d="M8 10v4M12 10v4M16 10v4"/>
|
||||
{% elif kind == "instagram" %}
|
||||
<rect x="4" y="4" width="16" height="16" rx="5"/>
|
||||
<circle cx="12" cy="12" r="3.5"/>
|
||||
<circle cx="17" cy="7" r="1" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "facebook" %}
|
||||
<path d="M14 8h3V5h-3c-2.8 0-5 2.2-5 5v2H7v3h3v8h4v-8h3l1-3h-4V9c0-.6.4-1 1-1z" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "threads" %}
|
||||
<path d="M12 4c3.5 0 6 2.2 6 5.5 0 3.8-3.2 6.5-7 6.5-2.2 0-4-.9-4-2.5S8.8 11 12 11s4-.4 4-1.5S14 7 12 7s-3 .8-3 2" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "tiktok" %}
|
||||
<path d="M10 6v9.5a3.5 3.5 0 11-2-3.2V9.5M14 6v5.5c1.2 0 2.2-.6 2.8-1.5V6h3v4.5c0 3.3-2.7 6-6 6s-6-2.7-6-6 2.7-6 6-6" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "discord" %}
|
||||
<path d="M8 8c1.5-1 4.5-1 6 0M7 16c2-1 8-1 10 0M9 10v1M15 10v1" fill="currentColor" stroke="none"/>
|
||||
<path d="M6 7c-1 2-1.5 5-1 9 1.5 1 4 1.5 7 1.5s5.5-.5 7-1.5c.5-4 .5-7-1-9-2-1.5-10-1.5-12 0z"/>
|
||||
{% elif kind == "orcid" %}
|
||||
<circle cx="12" cy="12" r="8"/>
|
||||
<path d="M9 10v6M12 10v6M15 10c0 2-1 3-1 3v3"/>
|
||||
{% elif kind == "scholar" %}
|
||||
<path d="M12 4L4 8v2c0 5 3.5 8.5 8 10 4.5-1.5 8-5 8-10V8l-8-4z"/>
|
||||
<path d="M12 12v6"/>
|
||||
{% elif kind == "medium" %}
|
||||
<circle cx="8" cy="12" r="4" fill="currentColor" stroke="none"/>
|
||||
<circle cx="16" cy="12" r="4" fill="currentColor" stroke="none"/>
|
||||
<circle cx="12" cy="12" r="4" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "substack" %}
|
||||
<path d="M5 6h14v3H5zM5 11h14v2l-7 5-7-5v-2z" fill="currentColor" stroke="none"/>
|
||||
{% elif kind == "rss" %}
|
||||
<path d="M5 5v14M5 15a7 7 0 017 7M5 11a3 3 0 013 3"/>
|
||||
{% else %}
|
||||
<path d="M10 13a5 5 0 007.5 4M14 6.5l2-2 2 2M10 10l-2 2 2 2"/>
|
||||
<circle cx="12" cy="12" r="9"/>
|
||||
{% endif %}
|
||||
</svg>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro website_icon() %}
|
||||
<svg class="contributor-social-icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="9"/>
|
||||
<path d="M3 12h18M12 3c2.5 2.8 4 6 4 9s-1.5 6.2-4 9M12 3c-2.5 2.8-4 6-4 9s1.5 6.2 4 9"/>
|
||||
</svg>
|
||||
{% endmacro %}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<section class="why-section why-section--community" id="community">
|
||||
<div class="container why-row why-row--art-end">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
{% include "partials/svg/why-craft.html" %}
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">Craft</p>
|
||||
<h2 class="section-title">Carefully crafted by brilliant people on Earth</h2>
|
||||
<p>
|
||||
A textbook is not a bulk download. It is years of lectures distilled, diagrams redrawn until they
|
||||
click, exercises tested in real classrooms, and arguments tightened by reviewers who care about
|
||||
getting it right.
|
||||
</p>
|
||||
<p>
|
||||
Open Free Books gathers that work from educators and researchers at institutions around the world —
|
||||
people who have already spent their careers making hard ideas legible. We publish their care
|
||||
<strong>openly</strong> so the next student inherits the best version, not a disposable draft.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container why-craft-logos-wrap" data-scroll-reveal-card>
|
||||
<p class="university-logos-label">Contributors and partners from leading universities include</p>
|
||||
{% include "partials/university-logos.html" %}
|
||||
</div>
|
||||
|
||||
{% include "partials/contributors-block.html" %}
|
||||
</section>
|
||||
119
themes/openfreebooks/templates/partials/contributors-block.html
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
{% set contributors_data = load_data(path="contributors.json", format="json") %}
|
||||
|
||||
<div class="community-contributors" id="contributors">
|
||||
<div class="container">
|
||||
<p class="section-kicker">People</p>
|
||||
<h2 class="section-title">Contributors</h2>
|
||||
<p class="contributors-intro">
|
||||
Open Free Books is written and maintained in the open. These are the people building and reviewing the textbooks.
|
||||
</p>
|
||||
<ul class="contributors-avatar-grid" role="list" data-scroll-reveal>
|
||||
{% for person in contributors_data.contributors %}
|
||||
{% set contributor_id = person.id | default(value=person.name | slugify) %}
|
||||
<li class="contributors-avatar-item" data-scroll-reveal-card>
|
||||
<button
|
||||
type="button"
|
||||
class="contributor-avatar-btn"
|
||||
data-contributor-dialog="contributor-dialog-{{ contributor_id }}"
|
||||
aria-label="View contributions by {{ person.name }}"
|
||||
aria-haspopup="dialog"
|
||||
>
|
||||
{% if person.avatar %}
|
||||
<img
|
||||
class="contributor-avatar-img"
|
||||
src="{{ get_url(path=person.avatar) | safe }}"
|
||||
alt=""
|
||||
width="48"
|
||||
height="48"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
{% else %}
|
||||
<span class="contributor-avatar-fallback" aria-hidden="true">{{ person.name | first }}</span>
|
||||
{% endif %}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% for person in contributors_data.contributors %}
|
||||
{% set contributor_id = person.id | default(value=person.name | slugify) %}
|
||||
<dialog id="contributor-dialog-{{ contributor_id }}" class="contributor-dialog">
|
||||
<form method="dialog" class="contributor-dialog-panel">
|
||||
<button type="submit" class="contributor-dialog-close" aria-label="Close">×</button>
|
||||
<div class="contributor-dialog-header">
|
||||
{% if person.avatar %}
|
||||
<img
|
||||
class="contributor-dialog-avatar"
|
||||
src="{{ get_url(path=person.avatar) | safe }}"
|
||||
alt=""
|
||||
width="80"
|
||||
height="80"
|
||||
decoding="async"
|
||||
/>
|
||||
{% endif %}
|
||||
<div class="contributor-dialog-identity">
|
||||
<h3 class="contributor-dialog-name">{{ person.name }}</h3>
|
||||
{% if person.job_title %}
|
||||
<p class="contributor-dialog-job">{{ person.job_title }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if person.education %}
|
||||
<p class="contributor-dialog-meta">
|
||||
<span class="contributor-meta-label">Education</span>
|
||||
{{ person.education }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if person.contributions %}
|
||||
<div class="contributor-dialog-contributions">
|
||||
<span class="contributor-meta-label">Contributes</span>
|
||||
<ul class="contributor-contribution-list" role="list">
|
||||
{% for item in person.contributions %}
|
||||
<li>{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if person.website or person.socials %}
|
||||
<div class="contributor-dialog-connect">
|
||||
<span class="contributor-meta-label">Connect</span>
|
||||
<ul class="contributor-social-list" role="list">
|
||||
{% if person.website %}
|
||||
<li>
|
||||
<a
|
||||
class="contributor-social-link"
|
||||
href="{{ person.website | safe }}"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Website"
|
||||
>
|
||||
{{ social::website_icon() | trim | safe }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if person.socials %}
|
||||
{% for link in person.socials %}
|
||||
<li>
|
||||
<a
|
||||
class="contributor-social-link"
|
||||
href="{{ social::social_href(social=link) | trim | safe }}"
|
||||
{% if link.type != "email" %}rel="noopener noreferrer"{% endif %}
|
||||
aria-label="{{ social::social_label(kind=link.type) | trim }}"
|
||||
>
|
||||
{{ social::social_icon(kind=link.type) | trim | safe }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</dialog>
|
||||
{% endfor %}
|
||||
|
||||
<p class="section-note">
|
||||
Want to contribute? Open a pull request on <a href="{{ config.extra.github_url | safe }}">GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
14
themes/openfreebooks/templates/partials/svg/why-craft.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Scholars and institutions worldwide</title>
|
||||
<circle cx="100" cy="92" r="46" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M54 92 C54 68 74 52 100 52 C126 52 146 68 146 92" fill="var(--accent)" fill-opacity="0.08" stroke="none"/>
|
||||
<path d="M54 92 C72 108 88 112 100 112 C112 112 128 108 146 92" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M54 92 C68 76 84 68 100 68 C116 68 132 76 146 92" fill="none" stroke="var(--soft-ink)" stroke-width="1" opacity="0.5"/>
|
||||
<circle cx="72" cy="148" r="10" fill="none" stroke="var(--ink)" stroke-width="1.2"/>
|
||||
<path d="M66 158 C66 150 69 146 72 146 C75 146 78 150 78 158 L78 168 L66 168 Z" fill="none" stroke="var(--ink)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<circle cx="100" cy="152" r="10" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M94 162 C94 154 97 150 100 150 C103 150 106 154 106 162 L106 172 L94 172 Z" fill="var(--accent)" fill-opacity="0.12" stroke="var(--accent)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<circle cx="128" cy="148" r="10" fill="none" stroke="var(--ink)" stroke-width="1.2"/>
|
||||
<path d="M122 158 C122 150 125 146 128 146 C131 146 134 150 134 158 L134 168 L122 168 Z" fill="none" stroke="var(--ink)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path d="M82 48 L88 36 M100 44 L100 32 M118 48 L112 36" stroke="var(--accent)" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
15
themes/openfreebooks/templates/partials/svg/why-html.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Paper and PDF versus interactive HTML</title>
|
||||
<rect x="24" y="48" width="56" height="72" rx="2" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M32 60 L72 60 M32 72 L68 72 M32 84 L64 84" stroke="var(--soft-ink)" stroke-width="1" stroke-linecap="round"/>
|
||||
<text x="52" y="108" text-anchor="middle" font-family="var(--mono)" font-size="9" fill="var(--muted)">PDF</text>
|
||||
<rect x="88" y="48" width="56" height="72" rx="2" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-dasharray="3 3" opacity="0.5"/>
|
||||
<path d="M96 60 L136 60 M96 72 L132 72" stroke="var(--soft-ink)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
<path d="M148 56 L148 160" stroke="var(--accent)" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M140 72 L156 72 L152 88 L144 88 Z" fill="var(--accent)" fill-opacity="0.2" stroke="var(--accent)" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<rect x="156" y="56" width="36" height="104" rx="4" fill="var(--card-bg)" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<path d="M164 72 L184 72 M164 84 L180 84 M164 96 L182 96" stroke="var(--accent)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<circle cx="174" cy="118" r="8" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M171 118 L173 120 L177 114" fill="none" stroke="var(--accent)" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="174" y="140" text-anchor="middle" font-family="var(--mono)" font-size="8" fill="var(--accent)"></></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -0,0 +1,11 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Student thinking with a textbook</title>
|
||||
<circle cx="100" cy="52" r="20" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M68 108 C68 86 82 74 100 74 C118 74 132 86 132 108 L132 132 L68 132 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<rect x="88" y="136" width="24" height="32" rx="2" fill="var(--accent)" fill-opacity="0.15" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<path d="M94 148 L106 148 M94 156 L106 156" stroke="var(--accent)" stroke-width="1" stroke-linecap="round"/>
|
||||
<path d="M44 64 Q28 48 36 32" fill="none" stroke="var(--soft-ink)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<path d="M40 36 L36 28 M44 34 L48 26" stroke="var(--soft-ink)" stroke-width="1" stroke-linecap="round"/>
|
||||
<text x="32" y="56" font-family="var(--font-heading)" font-size="14" fill="var(--accent)" font-style="italic">?</text>
|
||||
<path d="M156 72 L168 60 M160 88 L176 88" stroke="var(--muted)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
13
themes/openfreebooks/templates/partials/svg/why-not-ai.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Open textbook and the earth</title>
|
||||
<circle cx="100" cy="100" r="44" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M56 100 C56 72 76 56 100 56 C124 56 144 72 144 100 C144 128 124 144 100 144 C76 144 56 128 56 100 Z" fill="var(--accent)" fill-opacity="0.08" stroke="none"/>
|
||||
<path d="M56 92 C80 88 88 108 100 108 C112 108 120 88 144 92" fill="none" stroke="var(--accent)" stroke-width="1.2"/>
|
||||
<path d="M64 112 C88 118 96 98 108 98 C120 98 128 118 136 112" fill="none" stroke="var(--soft-ink)" stroke-width="1"/>
|
||||
<rect x="128" y="36" width="48" height="40" rx="6" fill="none" stroke="var(--muted)" stroke-width="1.2" stroke-dasharray="4 3" opacity="0.55"/>
|
||||
<path d="M136 52 L168 52 M136 60 L160 60" stroke="var(--muted)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
<path d="M120 36 L128 28" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<rect x="72" y="118" width="56" height="40" rx="2" fill="var(--card-bg)" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<path d="M80 132 L120 132 M80 142 L112 142" stroke="var(--ink)" stroke-width="1" stroke-linecap="round" opacity="0.5"/>
|
||||
<text x="100" y="154" text-anchor="middle" font-family="var(--mono)" font-size="7" fill="var(--accent)">OPEN</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Teacher guiding, open textbook</title>
|
||||
<circle cx="72" cy="56" r="18" fill="none" stroke="var(--ink)" stroke-width="1.5"/>
|
||||
<path d="M48 108 C48 88 60 78 72 78 C84 78 96 88 96 108 L96 130 L48 130 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M56 98 L88 98" stroke="var(--accent)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<path d="M108 72 L108 148 L168 148 L168 88 L148 72 Z" fill="var(--accent)" fill-opacity="0.1" stroke="var(--accent)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M116 92 L160 92 M116 108 L152 108 M116 124 L156 124" stroke="var(--ink)" stroke-width="1" stroke-linecap="round" opacity="0.6"/>
|
||||
<path d="M72 130 L72 160 M60 160 L84 160" stroke="var(--ink)" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M100 56 L132 72" stroke="var(--accent)" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="2 3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1,011 B |
|
|
@ -0,0 +1,12 @@
|
|||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true">
|
||||
<title>Stack of textbooks and cost</title>
|
||||
<rect x="48" y="118" width="104" height="12" rx="2" fill="var(--accent)" opacity="0.25"/>
|
||||
<path d="M52 118 L52 72 L88 64 L88 118 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M88 118 L88 64 L124 72 L124 118 Z" fill="none" stroke="var(--ink)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M124 118 L124 76 L160 84 L160 118 Z" fill="var(--accent)" fill-opacity="0.12" stroke="var(--accent)" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M56 72 L88 64 L88 68 L56 76 Z" fill="var(--accent)" fill-opacity="0.35" stroke="none"/>
|
||||
<circle cx="148" cy="52" r="28" fill="none" stroke="var(--accent)" stroke-width="1.5"/>
|
||||
<text x="148" y="58" text-anchor="middle" font-family="var(--mono)" font-size="18" fill="var(--accent)">$</text>
|
||||
<path d="M36 148 L164 148" stroke="var(--ink)" stroke-width="1" stroke-dasharray="4 4" opacity="0.4"/>
|
||||
<path d="M72 40 L72 28 M68 34 L76 34" stroke="var(--soft-ink)" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,47 @@
|
|||
<li class="university-logo university-logo--mit">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="MIT">
|
||||
<text x="60" y="34" text-anchor="middle" font-family="var(--mono)" font-size="22" font-weight="700" letter-spacing="0.12em" fill="currentColor">MIT</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--harvard">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Harvard University">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Harvard</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="8" letter-spacing="0.2em" fill="currentColor" opacity="0.75">UNIVERSITY</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--stanford">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Stanford University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="17" fill="currentColor">Stanford</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--berkeley">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="UC Berkeley">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="16" font-weight="700" fill="currentColor">Berkeley</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF CALIFORNIA</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--caltech">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Caltech">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">Caltech</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--princeton">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Princeton University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">Princeton</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--yale">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Yale University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="17" fill="currentColor">Yale</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--columbia">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Columbia University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Columbia</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cornell">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Cornell University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">Cornell</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<li class="university-logo university-logo--cmu">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Carnegie Mellon University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">CMU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--oxford">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Oxford">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="16" font-style="italic" fill="currentColor">Oxford</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.14em" fill="currentColor" opacity="0.7">UNIVERSITY</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cambridge">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Cambridge">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Cambridge</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--imperial">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Imperial College London">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="13" font-weight="700" fill="currentColor">Imperial</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">COLLEGE LONDON</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--lse">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="London School of Economics">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">LSE</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--eth">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="ETH Zurich">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">ETH</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--toronto">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Toronto">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Toronto</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--mcgill">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="McGill University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="16" fill="currentColor">McGill</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--hku">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The University of Hong Kong">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">HKU</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">THE UNIVERSITY OF HONG KONG</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<li class="university-logo university-logo--hkust">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Hong Kong University of Science and Technology">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="17" font-weight="700" letter-spacing="0.06em" fill="currentColor">HKUST</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--cuhk">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The Chinese University of Hong Kong">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">CUHK</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--nus">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="National University of Singapore">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">NUS</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.06em" fill="currentColor" opacity="0.7">NATIONAL UNIVERSITY OF SINGAPORE</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--tsinghua">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Tsinghua University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Tsinghua</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--pku">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Peking University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">PKU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--utokyo">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The University of Tokyo">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--font-heading)" font-size="14" fill="currentColor">Tokyo</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.1em" fill="currentColor" opacity="0.7">UNIVERSITY OF</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--anu">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Australian National University">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--mono)" font-size="20" font-weight="700" fill="currentColor">ANU</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--melbourne">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="University of Melbourne">
|
||||
<text x="60" y="30" text-anchor="middle" font-family="var(--font-heading)" font-size="15" fill="currentColor">Melbourne</text>
|
||||
</svg>
|
||||
</li>
|
||||
<li class="university-logo university-logo--iitb">
|
||||
<svg viewBox="0 0 120 48" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Indian Institute of Technology Bombay">
|
||||
<text x="60" y="22" text-anchor="middle" font-family="var(--mono)" font-size="18" font-weight="700" fill="currentColor">IIT</text>
|
||||
<text x="60" y="38" text-anchor="middle" font-family="var(--mono)" font-size="7" letter-spacing="0.08em" fill="currentColor" opacity="0.7">BOMBAY</text>
|
||||
</svg>
|
||||
</li>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<div class="university-carousel" role="region" aria-label="Universities represented among contributors">
|
||||
<div class="university-carousel-row university-carousel-row--forward">
|
||||
<div class="university-carousel-track">
|
||||
<ul class="university-carousel-group" role="list">
|
||||
{% include "partials/university-logos-row-1.html" %}
|
||||
</ul>
|
||||
<ul class="university-carousel-group" role="list" aria-hidden="true">
|
||||
{% include "partials/university-logos-row-1.html" %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="university-carousel-row university-carousel-row--reverse">
|
||||
<div class="university-carousel-track">
|
||||
<ul class="university-carousel-group" role="list">
|
||||
{% include "partials/university-logos-row-2.html" %}
|
||||
</ul>
|
||||
<ul class="university-carousel-group" role="list" aria-hidden="true">
|
||||
{% include "partials/university-logos-row-2.html" %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="university-carousel-row university-carousel-row--forward university-carousel-row--slow">
|
||||
<div class="university-carousel-track">
|
||||
<ul class="university-carousel-group" role="list">
|
||||
{% include "partials/university-logos-row-3.html" %}
|
||||
</ul>
|
||||
<ul class="university-carousel-group" role="list" aria-hidden="true">
|
||||
{% include "partials/university-logos-row-3.html" %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
108
themes/openfreebooks/templates/partials/why-sections.html
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<section class="why-section" id="why-textbook-costs">
|
||||
<div class="container why-row">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
{% include "partials/svg/why-textbook-costs.html" %}
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">The problem</p>
|
||||
<h2 class="section-title">Textbook fees hit families hard</h2>
|
||||
<p>
|
||||
In many countries, families pay hundreds of dollars per child each year for printed textbooks —
|
||||
on top of tuition, devices, and supplies. When money is tight, students share worn copies, skip
|
||||
buying books, or fall behind before the term begins.
|
||||
</p>
|
||||
<p>
|
||||
That cost is not a small inconvenience. It is a barrier to showing up prepared. Open Free Books
|
||||
exists so <strong>every learner can start the year with complete materials</strong>, not a receipt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-html">
|
||||
<div class="container why-row why-row--art-end">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
{% include "partials/svg/why-html.html" %}
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">The medium</p>
|
||||
<h2 class="section-title">Long live the web — not the PDF</h2>
|
||||
<p>
|
||||
Paper textbooks are fixed: one layout, one edition, no room to fix a typo until the next print run.
|
||||
Most “digital” versions are the same pages exported to PDF — still static, still hard to search,
|
||||
still impossible to embed a simulation or a live diagram.
|
||||
</p>
|
||||
<p>
|
||||
<strong>HTML on the open web</strong> is different. Chapters can link to sources, resize for any screen,
|
||||
include interactive figures, and improve over time. The book becomes something you use — not something
|
||||
you only carry.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-teachers">
|
||||
<div class="container why-row">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
{% include "partials/svg/why-teachers.html" %}
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">People & books</p>
|
||||
<h2 class="section-title">Replace textbooks, not teachers</h2>
|
||||
<p>
|
||||
A great teacher — whether in person or, someday, assisted by thoughtful tools — still sets pace,
|
||||
asks hard questions, and notices when a student is stuck. That relationship is not what we are
|
||||
trying to automate.
|
||||
</p>
|
||||
<p>
|
||||
Textbooks are different. They are reference and structure: explanations, exercises, diagrams.
|
||||
Those can be <strong>free, forkable, and kept current on the web</strong> so teachers spend less
|
||||
time hunting permissions and more time teaching.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-learning">
|
||||
<div class="container why-row why-row--art-end">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
{% include "partials/svg/why-learning.html" %}
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">Agency</p>
|
||||
<h2 class="section-title">Do not outsource your learning</h2>
|
||||
<p>
|
||||
Finishing a chapter is not the same as understanding it. Skimming for answers, copying worked
|
||||
solutions, or rushing to the end leaves gaps that show up on the next test — and in the years after.
|
||||
</p>
|
||||
<p>
|
||||
Read slowly. Try the problems before you peek at hints. Sit with confusion long enough to name what
|
||||
you do not get. <strong>The textbook is a guide; the thinking still has to be yours.</strong> That habit
|
||||
is what school is really training, whether the subject is algebra or history.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="why-section" id="why-not-ai">
|
||||
<div class="container why-row">
|
||||
<div class="why-art" data-scroll-reveal-card>
|
||||
{% include "partials/svg/why-not-ai.html" %}
|
||||
</div>
|
||||
<div class="why-copy" data-scroll-reveal-card>
|
||||
<p class="section-kicker">Planet</p>
|
||||
<h2 class="section-title">Open textbooks are gentler on the Earth</h2>
|
||||
<p>
|
||||
Core curriculum changes slowly. Newton’s laws, the structure of a cell, and how fractions work do not
|
||||
need a new AI run every September. Regenerating the same chapter thousands of times burns electricity
|
||||
in data centers — for text that could have been written once and shared openly.
|
||||
</p>
|
||||
<p>
|
||||
<strong>One carefully reviewed book</strong>, published on the web for everyone, means less redundant
|
||||
compute, less e-waste from disposable PDFs, and fewer trucks moving heavy print runs. Students get
|
||||
stable material they can trust; the environment gets less waste from doing the same work over and over.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||