@nicohaberkorn/sinn 0.0.0-stage → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -2
- package/dist/dev-all/color-comp.css +39 -0
- package/dist/dev-all/color-pal-nh-pal-hct.css +2052 -0
- package/dist/dev-all/color-pal-nh-pal-hsl.css +1966 -0
- package/dist/dev-all/color-pal-sn-pal-hct.css +2052 -0
- package/dist/dev-all/color-pal-sn-pal-hsl.css +1966 -0
- package/dist/dev-all/color-ref-nh-ref-amber.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-blue.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-brown.css +151 -0
- package/dist/dev-all/color-ref-nh-ref-cyan.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-emerald.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-fuchsia.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-green.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-indigo.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-lightblue.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-lime.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-orange.css +122 -0
- package/dist/dev-all/color-ref-nh-ref-pink.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-porcelain.css +151 -0
- package/dist/dev-all/color-ref-nh-ref-purple.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-red.css +122 -0
- package/dist/dev-all/color-ref-nh-ref-rose.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-slate.css +151 -0
- package/dist/dev-all/color-ref-nh-ref-stone.css +151 -0
- package/dist/dev-all/color-ref-nh-ref-teal.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-truegray.css +156 -0
- package/dist/dev-all/color-ref-nh-ref-violet.css +123 -0
- package/dist/dev-all/color-ref-nh-ref-yellow.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-amber.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-blue.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-brown.css +151 -0
- package/dist/dev-all/color-ref-sn-ref-cyan.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-emerald.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-fuchsia.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-green.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-indigo.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-lightblue.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-lime.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-orange.css +122 -0
- package/dist/dev-all/color-ref-sn-ref-pink.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-porcelain.css +151 -0
- package/dist/dev-all/color-ref-sn-ref-purple.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-red.css +122 -0
- package/dist/dev-all/color-ref-sn-ref-rose.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-slate.css +151 -0
- package/dist/dev-all/color-ref-sn-ref-stone.css +151 -0
- package/dist/dev-all/color-ref-sn-ref-teal.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-truegray.css +156 -0
- package/dist/dev-all/color-ref-sn-ref-violet.css +123 -0
- package/dist/dev-all/color-ref-sn-ref-yellow.css +123 -0
- package/dist/dev-all/color-role-dark-calm.css +38 -0
- package/dist/dev-all/color-role-dark-normal.css +38 -0
- package/dist/dev-all/color-role-dark-playful.css +38 -0
- package/dist/dev-all/color-role-dark-vibrant.css +38 -0
- package/dist/dev-all/color-role-light-calm.css +38 -0
- package/dist/dev-all/color-role-light-normal.css +38 -0
- package/dist/dev-all/color-role-light-playful.css +38 -0
- package/dist/dev-all/color-role-light-vibrant.css +38 -0
- package/dist/dev-all/color-role-marginalia-dark.css +41 -0
- package/dist/dev-all/color-role-marginalia-light.css +41 -0
- package/dist/dev-all/color-role-monochrome-dark.css +41 -0
- package/dist/dev-all/color-role-monochrome-light.css +41 -0
- package/dist/dev-all/color-role-solarpunk-dark.css +41 -0
- package/dist/dev-all/color-role-solarpunk-light.css +41 -0
- package/dist/dev-all/index.css +54 -0
- package/dist/dev-all/type-pal.css +104 -0
- package/dist/dev-all/type-ref-expression-bad.css +28 -0
- package/dist/dev-all/type-ref-expression-editorial.css +34 -0
- package/dist/dev-all/type-ref-expression-good.css +28 -0
- package/dist/dev-all/type-ref-expression-natural.css +34 -0
- package/dist/dev-all/type-ref-typescale-default.css +26 -0
- package/dist/dev-all/type-ref-typescale-smaller.css +26 -0
- package/dist/dev-all/type-ref-typescale-smallest.css +26 -0
- package/dist/dev-all/type-role-base.css +54 -0
- package/dist/dev-all/type-role-expression-editorial.css +24 -0
- package/dist/dev-all/type-role-expression-modern.css +7 -0
- package/dist/dev-all/type-role-expression-natural.css +18 -0
- package/dist/dev-all/type-role-expression-technical.css +17 -0
- package/dist/dev-all/type-role-leading-comfy.css +14 -0
- package/dist/dev-all/type-role-leading-default.css +14 -0
- package/dist/foundation.css +81 -0
- package/dist/marginalia/dark.css +106 -0
- package/dist/marginalia/root.css +163 -0
- package/dist/modern-classic/dark.css +106 -0
- package/dist/modern-classic/root.css +163 -0
- package/dist/motion/index.d.ts +2672 -0
- package/dist/motion/index.js +441 -0
- package/dist/motion/index.js.map +1 -0
- package/dist/motion.css +186 -0
- package/dist/primitives.css +406 -0
- package/dist/solarpunk/dark.css +106 -0
- package/dist/solarpunk/root.css +163 -0
- package/dist/technology/dark.css +106 -0
- package/dist/technology/root.css +163 -0
- package/dist/trace/marginalia.json +1 -0
- package/dist/trace/modern-classic.json +1 -0
- package/dist/trace/scales.json +1 -0
- package/dist/trace/solarpunk.json +1 -0
- package/dist/trace/technology.json +1 -0
- package/package.json +62 -4
package/README.md
CHANGED
|
@@ -1,3 +1,282 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nicohaberkorn/sinn
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The design system behind [nicohaberkorn.com](https://nicohaberkorn.com): layered design tokens (color, typography, layout, motion), intrinsic layout primitives and a small set of motion components. The whole visual identity is expressed as tokens, so the site can be rebranded by switching a preset rather than rewriting CSS.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i @nicohaberkorn/sinn
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Intent
|
|
10
|
+
|
|
11
|
+
Three ideas shape the system.
|
|
12
|
+
|
|
13
|
+
1. **Components never see raw values.** A component asks for a *role*, such as "text on a surface", "a pencil stroke" or "the display typeface". It never asks for a colour value, a font name or a pixel size. The preset decides what that role looks like. A rebrand is then a new preset plus, at most, a few new roles, with no component changes.
|
|
14
|
+
2. **Explore everything, ship one thing.** The source describes a large design space: 22 hues across 2 base palettes, 4 contrast modes, several typefaces, type scales and line-heights. That is useful while designing. A visitor, however, only needs the single combination the site uses. The build resolves that combination ahead of time and ships only its final values, plus the light/dark switch, which is the one axis that stays live at runtime. The full, switchable space is still available as a separate development-only stylesheet.
|
|
15
|
+
3. **Space comes from type, and layout adapts to its container.** Every spacing value derives from one line-height unit. The layout primitives adapt to the space they are given (they are intrinsic) rather than to viewport breakpoints.
|
|
16
|
+
|
|
17
|
+
## How tokens are layered
|
|
18
|
+
|
|
19
|
+
Tokens resolve in four layers. Each layer may only reference the layer below it.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
PAL raw values --sn-pal-teal-60: oklch(0.6389 0.1202 175.29) --sn-pal-type-family-fraunces: 'Fraunces', …
|
|
23
|
+
↓
|
|
24
|
+
REF the preset's choices --sn-ref-primary-40: <a pal step> --sn-ref-type-family-serif: <a pal family>
|
|
25
|
+
↓
|
|
26
|
+
ROLE what things mean --sn-role-color-on-surface --sn-role-type-prose-family
|
|
27
|
+
↓
|
|
28
|
+
COMP component specifics --sn-comp-color-filled-button-background --sn-comp-color-stamp-border
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Only ROLE and COMP tokens are public.** A preset build removes PAL and REF entirely, so neither exists at runtime. The one exception is `--sn-ref-type-family-mono`, which the typography base styles use for `code`. Build components against `--sn-role-*` and `--sn-comp-color-*` only.
|
|
32
|
+
|
|
33
|
+
### Naming
|
|
34
|
+
|
|
35
|
+
Every custom property starts with `sn`, the name space, so nothing collides with another system. After it comes the level, the attribute and the token, and each part says what it is:
|
|
36
|
+
|
|
37
|
+
- `--sn-role-color-*`, `--sn-role-type-*` and `--sn-comp-color-*`: level, attribute, token. The level is the token level the value sits at.
|
|
38
|
+
- Foundation tokens have no preset and no levels of their own, so they are `sn` plus the attribute: `--sn-space-*`, `--sn-radius-*`, `--sn-layer-*`, `--sn-motion-*`, `--sn-measure-*`, `--sn-cq-*`, `--sn-ratio-*`, `--sn-focus-*`, `--sn-gutter`, `--sn-page-max-width` and `--sn-line-height-unit`.
|
|
39
|
+
- Palettes are named after the colour model they were generated in (`sn-pal-hct`, `sn-pal-hsl`), and accent sets after what they do (`monochrome`, `solarpunk`, `marginalia`), so a name tells you why it exists.
|
|
40
|
+
|
|
41
|
+
Migrating from 0.8: add the `sn-` prefix to `--space-*`, `--radius-*`, `--layer-*`, `--motion-*`, `--measure*`, `--cq-*`, `--ratio-*`, `--gutter`, `--page-max-width`, `--focus-outline-*` and `--line-height-unit`; rename `--color-comp-*` to `--sn-comp-color-*` and `--color-focus` to `--sn-focus-color`. Removed: `--color-role-background` and `--color-role-on-background` (unused duplicates of role tokens) and the legacy `--font-size-h1` … `-caption` aliases (use `--sn-role-type-size-*`).
|
|
42
|
+
|
|
43
|
+
### Namespace and token space (the model behind the names)
|
|
44
|
+
|
|
45
|
+
A token name has two parts. The **name space** says where it lives: `sn` (the system), a **theme** (the preset) and a **domain** (website, webapp, d-app, m-app). The **token space** says what it is: a design attribute (color, text) and a token level.
|
|
46
|
+
|
|
47
|
+
| Level | Diagram name | Purpose (color / text) | Mode criterion | Today |
|
|
48
|
+
|---|---|---|---|---|
|
|
49
|
+
| comp | comp | variation / compose | composition | `--sn-comp-color-*` (color only) |
|
|
50
|
+
| role | sys | combination / combine | context: brightness / screen real estate | `--sn-role-color-*`, `--sn-role-type-*` |
|
|
51
|
+
| ref | ref | allocation / allocate | selection | build-time only |
|
|
52
|
+
| pal | pal | restriction / restrict | expression | build-time only |
|
|
53
|
+
| scale | scale | operation / operate | structure | generative: tonal scale 0-100, type ratios (data in `trace/scales.json`) |
|
|
54
|
+
| space | space | specification / specify | format | generative: oklch for colour, px to rem and clamp() for type |
|
|
55
|
+
| seed | seed | inception / incept | input | generative: a family's key colour, a scale's base size |
|
|
56
|
+
|
|
57
|
+
Theme is not part of any variable name: a preset build fixes it, so the same names carry different values per preset. Domain is documented but not yet built: only `website` exists. The intent is that colors, seed and palette are shared across domains while comp (and parts of role) differ, so a button in a web app is denser than on the website but visibly the same family.
|
|
58
|
+
|
|
59
|
+
### Generative levels: seed, space, scale
|
|
60
|
+
|
|
61
|
+
The seven levels split in two. **Applied levels** make decisions: comp, role, ref and pal. **Generative levels** create the possibilities the applied levels choose from: seed (inception, input), space (specification, format) and scale (operation, structure). Read from the bottom, a value is a seed, put into a space, run through a scale, curated into a palette, allocated by a preset, given a meaning by a role and specialised by a component.
|
|
62
|
+
|
|
63
|
+
| | Colour | Type |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| **seed** | a hue and chroma per ramp (recorded for the `sn-pal-hct` families), a key colour | a base size in px: 16, 18, 54, 102 … |
|
|
66
|
+
| **space** | oklch (every tone is written as `oklch(L C H)`, L and C to three decimals, H in whole degrees) | px converted to rem (px / 16), and `clamp()` for fluid sizes |
|
|
67
|
+
| **scale** | a tonal scale of 0 to 100, where a tone tracks CIE L* lightness, so one step is equally light in every hue | a named ratio applied to the base: step n = base x ratio^(n-1) |
|
|
68
|
+
|
|
69
|
+
The type palette holds 14 named scales of five steps: three golden-ratio scales (bases 54, 71 and 102 px, in whole pixels), a perfect fourth at 20 px, a major third at 18 px, minor thirds at 16 and 14 px and major seconds at 18, 16, 14, 12, 11, 9 and 8 px. The ref level turns two scale steps into one fluid size: `clamp(min, slope x 100vw + intercept, max)`, growing linearly between a 20rem and an 80rem viewport, where slope = (max - min) / (80rem - 20rem) and intercept = min - slope x 20rem. Which step each end takes is written in `tokens/type/scale-map.json`, and `scripts/build-typescale.mjs` generates `tokens/type/ref/typescale-*.json` from it, so no size is a free-standing number. Each group takes one scale per end (steps 5 to 1 from largest to smallest):
|
|
70
|
+
|
|
71
|
+
| Group | At 320px | At 1280px |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| display | golden ratio, 54px seed | golden ratio, 102px seed |
|
|
74
|
+
| heading | minor third, 16px seed | perfect fourth, 20px seed |
|
|
75
|
+
| body | major second, 12px seed | major second, 16px seed |
|
|
76
|
+
| label | major second, 8px seed | major second, 11px seed |
|
|
77
|
+
|
|
78
|
+
Every size is within a pixel of the value it had before it was tied to a scale. `--sn-role-type-size-subheading` is `heading-smallest`, so a subheading is never larger than any heading. The `smaller` and `smallest` type scales are copies of `default` for now.
|
|
79
|
+
|
|
80
|
+
Each palette family has three tonal ramps of up to 31 tones: the family (`teal`), a desaturated companion (`tealvariant`) and a tinted neutral (`tealgray`). The palettes are named after the colour model they were generated in. `sn-pal-hct` was generated in HCT (hue, chroma, tone) and follows CIE L* closely, so tone 40 is L* 40 within a fraction; the hue and chroma noted for each ramp are in `tokens/color/seeds/seeds.json`. `sn-pal-hsl` was generated in HSL with a shared tone-lightness curve and is more saturated; its neutrals are a straight percentage of 255. In both palettes lightness rises with every tone, and in `sn-pal-hct` every tone is within 2 L* of its number; tones that broke this were corrected, and only those.
|
|
81
|
+
|
|
82
|
+
`dist/trace/scales.json` exposes all of this as data: the type scales with ratio and base, every fluid ref size with its min, max and where each end comes from, and every colour ramp. Import it as `@nicohaberkorn/sinn/trace/scales.json`.
|
|
83
|
+
|
|
84
|
+
### Resolution trace (`trace/<preset>.json`)
|
|
85
|
+
|
|
86
|
+
Because pal and ref never ship as CSS, each build also writes `dist/trace/<preset>.json`: for every public token, per theme, the chain it resolves through (comp, role, ref, pal, literal) with the value at each step. Import it as `@nicohaberkorn/sinn/trace/marginalia.json`. Documentation, not runtime styling.
|
|
87
|
+
|
|
88
|
+
### Axes
|
|
89
|
+
|
|
90
|
+
A preset is one value chosen per axis:
|
|
91
|
+
|
|
92
|
+
| Axis | What it controls | Values |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `colorPal` | Base palette (the PAL layer) | `sn-pal-hsl` (HSL, more saturated), `sn-pal-hct` (HCT, follows CIE L*) |
|
|
95
|
+
| `colorRef` | Primary hue driving the REF color ramps | 22 hues, e.g. `sn-ref-truegray`, `sn-ref-blue`, `sn-ref-green` |
|
|
96
|
+
| `mode` | Contrast mode | `normal`, `calm`, `playful`, `vibrant` |
|
|
97
|
+
| `vibe` | Accent strategy (the `accent-1`…`accent-7` colours) | `solarpunk`, `monochrome`, `marginalia` |
|
|
98
|
+
| `refExpression` | Typefaces, weights and tracking | `natural`, `good`, `bad` (a QA placeholder), `editorial` |
|
|
99
|
+
| `refTypescale` | Fluid type scale | `default`, `smaller`, `smallest` |
|
|
100
|
+
| `expression` | Which typeface each role uses | `natural`, `technical`, `modern`, `editorial` |
|
|
101
|
+
| `leading` | Line-heights | `default`, `comfy` |
|
|
102
|
+
| `theme` | Light or dark | the only axis switched at runtime, via `[data-theme]` |
|
|
103
|
+
|
|
104
|
+
## Presets
|
|
105
|
+
|
|
106
|
+
| Preset | Look | Axes |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| **`marginalia`** | Monochrome editorial base with one teal "pencil" that edits, circles, highlights and stamps. Bricolage Grotesque for display and UI, Fraunces for prose, Geist Mono for labels, Caveat for handwritten notes. **This is what nicohaberkorn.com ships.** | `sn-pal-hct` · `sn-ref-truegray` · `calm` · `marginalia` · `editorial` / `editorial` · `default` · `comfy` |
|
|
109
|
+
| `modern-classic` | Inter everywhere, Merriweather prose, single blue accent. | `sn-pal-hsl` · `sn-ref-blue` · `normal` · `monochrome` · `modern` / `natural` · `default` · `default` |
|
|
110
|
+
| `technology` | Monospaced headings on a neutral grey base. | `sn-pal-hct` · `sn-ref-truegray` · `normal` · `technology` · `technical` / `natural` · `smaller` · `default` |
|
|
111
|
+
| `solarpunk` | Green base with a full color-wheel of section accents, serif display. | `sn-pal-hsl` · `sn-ref-green` · `calm` · `solarpunk` · `natural` / `natural` · `default` · `comfy` |
|
|
112
|
+
|
|
113
|
+
Each preset ships as two files: `root.css` (`:root, [data-theme="light"]`) and `dark.css` (`[data-theme="dark"]`). Always set `data-theme` on `<html>` explicitly.
|
|
114
|
+
|
|
115
|
+
## Using it
|
|
116
|
+
|
|
117
|
+
```css
|
|
118
|
+
@import "@nicohaberkorn/sinn/foundation.css"; /* space, measure, layers, radius, motion: preset-independent */
|
|
119
|
+
@import "@nicohaberkorn/sinn/primitives.css"; /* layout primitives + typography base */
|
|
120
|
+
@import "@nicohaberkorn/sinn/motion.css"; /* motion components + annotation types */
|
|
121
|
+
@import "@nicohaberkorn/sinn/presets/marginalia/root.css";
|
|
122
|
+
@import "@nicohaberkorn/sinn/presets/marginalia/dark.css";
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<html data-theme="light"> … </html>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The preset files contain literal font-family names such as `'Bricolage Grotesque'`. The consuming app loads the font files itself, for example with `next/font` or `@font-face`, under exactly those family names.
|
|
130
|
+
|
|
131
|
+
For design exploration, import `@nicohaberkorn/sinn/dev-all.css` instead of a preset. It keeps the full `[data-*]` cascade, so every axis can be switched live via attributes on `<html>` (`data-color-pal`, `data-color-ref`, `data-mode`, `data-vibe`, `data-expression`, `data-ref-expression`, `data-ref-typescale`, `data-leading`, `data-theme`). It is large; load it only in development.
|
|
132
|
+
|
|
133
|
+
## Token reference
|
|
134
|
+
|
|
135
|
+
### Color roles: `--sn-role-color-*`
|
|
136
|
+
|
|
137
|
+
Surfaces and text follow Material-style role pairs:
|
|
138
|
+
|
|
139
|
+
- `primary` / `on-primary`, `primary-container` / `on-primary-container`, `inverse-primary`
|
|
140
|
+
- `secondary` / `on-secondary`, with `-container` variants
|
|
141
|
+
- `surface` / `on-surface`, plus `surface-variant`, `on-surface-variant` (secondary text), and `surface-dim` / `-bright`
|
|
142
|
+
- `surface-container-{lowest,low,,high,highest}`
|
|
143
|
+
- `inverse-surface` / `inverse-on-surface` / `inverse-on-surface-variant` (secondary text on inverse bands) / `inverse-primary`
|
|
144
|
+
- `outline` / `outline-variant`
|
|
145
|
+
- `error` / `on-error`, with containers
|
|
146
|
+
- `shadow`, `disabled` / `on-disabled`
|
|
147
|
+
- **Accents**, a full set per number, mirroring `primary` (below)
|
|
148
|
+
|
|
149
|
+
Two gotchas: `outline` is nearly invisible against surfaces, so use `outline-variant` for control borders. `surface` always means a neutral background; accents never use the words `surface`, `outline` or `variant`.
|
|
150
|
+
|
|
151
|
+
### Accents: `accent-1` … `accent-7`
|
|
152
|
+
|
|
153
|
+
Each accent is a complete role set in the same shape as `primary`:
|
|
154
|
+
|
|
155
|
+
| `primary` | Accent | Meaning |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `primary` | `accent-N` | the colour itself; safe as text or stroke on `surface` |
|
|
158
|
+
| `on-primary` | `on-accent-N` | content on a filled accent |
|
|
159
|
+
| `primary-container` | `accent-N-container` | soft tinted fill |
|
|
160
|
+
| `on-primary-container` | `on-accent-N-container` | content on that fill |
|
|
161
|
+
| `inverse-primary` | `inverse-accent-N` | the accent on `inverse-surface` (the bands) |
|
|
162
|
+
|
|
163
|
+
What each number is depends on the vibe: `solarpunk` is a colour wheel (green, teal, blue, violet, fuchsia, grey, teal), `monochrome` is single-hue (every accent is the primary ramp), and `marginalia` has teal, blue and violet in 1-3 and monochrome in 4-7. Tones follow one recipe (light: 40 / 100 / 90 / 10 / 80; dark: 70 / 20 / 30 / 90 / 40), so any hue works.
|
|
164
|
+
|
|
165
|
+
### Type roles beyond the voices
|
|
166
|
+
|
|
167
|
+
| Token | Use |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `type-script-{family,weight,tracking,line-height}` | Handwriting voice (Caveat in `marginalia`) |
|
|
170
|
+
| `type-display-stretch` | Display width (`90%` in `editorial`) |
|
|
171
|
+
| `type-label-transform` | Label case (`uppercase` in `editorial`) |
|
|
172
|
+
|
|
173
|
+
### Component tokens: `--sn-comp-color-*`
|
|
174
|
+
|
|
175
|
+
- Buttons: `filled-`, `tonal-`, `outline-`, `text-` and `elevated-button-{background,border,shadow}`, plus the matching `on-*` tokens
|
|
176
|
+
- `hover`, `disabled-button-background`
|
|
177
|
+
- Annotation layer: `mark-stroke` (`accent-1`), `mark-stroke-on-inverse` (`inverse-accent-1`), `mark-highlight-background` (`accent-1-container`), `on-mark-highlight-background` (`on-accent-1-container`), `annotation-text` and `stamp-{border,text}` (`accent-1`), `stamp-neutral-{border,text}` (`on-surface`). These are thin aliases: point a mark at `accent-2` and it changes colour.
|
|
178
|
+
|
|
179
|
+
### Type roles: `--sn-role-type-*`
|
|
180
|
+
|
|
181
|
+
- Voices `display`, `heading`, `subheading`, `body`, `prose` (long-form reading), `label`, `nav` and `script`, each with `-family`, `-weight`, `-tracking` and `-line-height`. `prose` is `body` with a reading typeface: it inherits body's sizes, weight, tracking and line height, so only the family differs
|
|
182
|
+
- Fluid sizes `size-{display,heading,body,label}-{largest,large,medium,small,smallest}` and `size-subheading`
|
|
183
|
+
|
|
184
|
+
### Foundation (`foundation.css`, one file for every preset)
|
|
185
|
+
|
|
186
|
+
| Group | Tokens |
|
|
187
|
+
|---|---|
|
|
188
|
+
| Space | `--sn-space-3xs` … `--sn-space-4xl`, all derived from `--sn-line-height-unit` (1.5rem). Fluid pairs `--sn-space-xs-s` … `--sn-space-2xl-3xl` |
|
|
189
|
+
| Measure | `--sn-measure-narrow` (45ch), `-body` (65ch), `-wide` (85ch), `-max` (110ch) |
|
|
190
|
+
| Page | `--sn-page-max-width`, `--sn-gutter` |
|
|
191
|
+
| Container sizes | `--sn-cq-compact`, `-medium`, `-wide`, `-full` |
|
|
192
|
+
| Ratios | `--sn-ratio-widescreen`, `-landscape`, `-standard`, `-square`, `-portrait`, `-tall` |
|
|
193
|
+
| Layers | `--sn-layer-base` … `--sn-layer-tooltip` (0–70). Always pair with `isolation: isolate` |
|
|
194
|
+
| Radius | `--sn-radius-xs` (2px), `-s` (6px), `-m` (8px), `-pill` |
|
|
195
|
+
| Motion | `--sn-motion-duration-{instant,fast,medium,slow,stamp,draw-intro,draw-follow}`, `--sn-motion-ease-{out,in-out,overshoot,linear}`, `--sn-motion-draw-{start,end}`, `--sn-motion-stamp-trigger`, `--sn-motion-stagger`, `--sn-motion-intro-gap`. Under `prefers-reduced-motion` every duration becomes `0ms` |
|
|
196
|
+
| Focus | `--sn-focus-outline-width`, `--sn-focus-outline-offset`, `--sn-focus-color` |
|
|
197
|
+
|
|
198
|
+
## Layout primitives (`primitives.css`)
|
|
199
|
+
|
|
200
|
+
These are hand-authored intrinsic layouts in the Every Layout style. They respond to the space they are given, not to the viewport.
|
|
201
|
+
|
|
202
|
+
| Class | Purpose |
|
|
203
|
+
|---|---|
|
|
204
|
+
| `.stack` | Vertical flow with a consistent `gap` (`data-gap="xs|s|l|xl"`) |
|
|
205
|
+
| `.cluster` | Wrapping horizontal group (`data-justify`) |
|
|
206
|
+
| `.switcher` | Row that becomes a column below a threshold |
|
|
207
|
+
| `.auto-grid` | `repeat(auto-fill, minmax(min(100%, 18rem), 1fr))` (`data-min`) |
|
|
208
|
+
| `.with-sidebar` | Sidebar plus content that wraps when the content would get too narrow |
|
|
209
|
+
| `.center-wrap`, `.center--text`, `.center--intrinsic` | Max-width, centering and gutters |
|
|
210
|
+
| `.cover` | Vertically centered principal element |
|
|
211
|
+
| `.frame`, `.page-grid`, `.full-bleed`, `.flow` | Media frames, page grid, bleeding out of the grid, prose rhythm |
|
|
212
|
+
|
|
213
|
+
## Motion (`@nicohaberkorn/sinn/motion` + `motion.css`)
|
|
214
|
+
|
|
215
|
+
React components (peer dependencies `react` ≥ 19; built on `motion`):
|
|
216
|
+
|
|
217
|
+
- `Button`, `ButtonLink`: press and hover physics
|
|
218
|
+
- `ChromaticTextReveal`, `SharedLayoutBg`
|
|
219
|
+
- Shared easing constants: `EASE_OUT` and `EASE_IN_OUT`, which match `--sn-motion-ease-out` and `--sn-motion-ease-in-out`, and `SPRING_LAYOUT` and `SPRING_PRESS`
|
|
220
|
+
|
|
221
|
+
The annotation layer draws a reader's marks as the page scrolls. It is framework-agnostic CSS plus a small engine:
|
|
222
|
+
|
|
223
|
+
| Class | Draws as |
|
|
224
|
+
|---|---|
|
|
225
|
+
| `.sn-mark-highlight` | Highlighter swipe |
|
|
226
|
+
| `.sn-mark-underline` | Pen line |
|
|
227
|
+
| `.sn-mark-circle` | Hand-drawn ring |
|
|
228
|
+
| `.sn-mark-strike` | An edit through a word |
|
|
229
|
+
| `.sn-mark-margin` | Double stroke in the margin |
|
|
230
|
+
| `.sn-mark-box` | Hand-drawn frame around a key passage |
|
|
231
|
+
| `.sn-annotation` | Handwritten note, revealed as if written |
|
|
232
|
+
| `.sn-draw-path` | Any SVG stroke (add `pathLength="1"`) |
|
|
233
|
+
| `.sn-scribble` | Wavy underline |
|
|
234
|
+
| `.sn-stamp`, `.sn-stamp--neutral` | Status label that lands once |
|
|
235
|
+
|
|
236
|
+
Mark an element with `data-sn-draw` so the engine animates it. Add `data-sn-delay="ms"` to use a timed intro instead of scroll-linked drawing, typically for marks above the fold. Mark stamps with `data-sn-stamp`.
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
import { createScrollDraw, setMarksVisible, restoreMarks } from '@nicohaberkorn/sinn/motion'
|
|
240
|
+
// React: useScrollDraw(deps), <Mark kind="highlight|underline|circle|strike">, <Annotation>, <Stamp tone>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`.sn-on-inverse` switches the pencil for inverse bands. `[data-marks="hidden"]` on `<html>` hides the reader's marks; `setMarksVisible()` sets it and `restoreMarks()` restores the saved choice. Under reduced motion every mark renders fully drawn.
|
|
244
|
+
|
|
245
|
+
## Source layout
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
tokens/ DTCG JSON, the source of truth
|
|
249
|
+
color/pal/{sn-pal-hsl,sn-pal-hct}/<hue>.json
|
|
250
|
+
color/ref/sn-ref-<hue>.json
|
|
251
|
+
color/role/{light,dark}-{normal,calm,playful,vibrant}.json complete per mode
|
|
252
|
+
color/role/accent-<vibe>-{light,dark}.json the seven accent sets per vibe
|
|
253
|
+
color/comp/comp.json
|
|
254
|
+
type/pal.json, type/ref/*.json, type/role/*.json
|
|
255
|
+
layout/layout.json, motion/motion.json
|
|
256
|
+
registry.js axis → value → source files
|
|
257
|
+
presets.js preset definitions and BUILT_PRESETS
|
|
258
|
+
scripts/ build-tokens (presets + foundation), build-dev-all, build-primitives, build-motion-css
|
|
259
|
+
src/primitives/ hand-authored layout CSS
|
|
260
|
+
src/motion/ React components, scroll-draw engine, motion CSS
|
|
261
|
+
dist/ generated; never edit by hand
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Development
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
npm install
|
|
268
|
+
npm run build # dist/: foundation, primitives, presets, dev-all, motion
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Adding a preset
|
|
272
|
+
|
|
273
|
+
1. Pick a value for each axis in `presets.js` and add the name to `BUILT_PRESETS`.
|
|
274
|
+
2. If you need a value that doesn't exist yet, add its token file under `tokens/` and register it in `tokens/registry.js`. A new vibe needs `accent-<vibe>-{light,dark}.json`, plus a selector in `scripts/build-dev-all.mjs`.
|
|
275
|
+
3. Prefer existing role shapes over new ones: a new colour is an accent set (`accent-N`, `on-accent-N`, …) in the vibe files, not a new group. A role every preset needs goes in **every** role-mode file, so each preset keeps shipping a complete set of tokens.
|
|
276
|
+
4. Run `npm run build` and add the preset's two files to `exports` in `package.json`.
|
|
277
|
+
|
|
278
|
+
A new preset must not change the resolved values of any existing preset. To check, diff `dist/<preset>/*.css` before and after the build.
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT © Nico Haberkorn
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Do not edit directly, this file was auto-generated.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
--sn-comp-color-filled-button-border: transparent;
|
|
7
|
+
--sn-comp-color-filled-button-shadow: transparent;
|
|
8
|
+
--sn-comp-color-tonal-button-border: transparent;
|
|
9
|
+
--sn-comp-color-tonal-button-shadow: transparent;
|
|
10
|
+
--sn-comp-color-outline-button-background: transparent;
|
|
11
|
+
--sn-comp-color-outline-button-shadow: transparent;
|
|
12
|
+
--sn-comp-color-text-button-background: transparent;
|
|
13
|
+
--sn-comp-color-text-button-border: transparent;
|
|
14
|
+
--sn-comp-color-text-button-shadow: transparent;
|
|
15
|
+
--sn-comp-color-filled-button-background: var(--sn-role-color-primary);
|
|
16
|
+
--sn-comp-color-on-filled-button-background: var(--sn-role-color-on-primary);
|
|
17
|
+
--sn-comp-color-on-tonal-button-background: var(--sn-role-color-on-primary-container);
|
|
18
|
+
--sn-comp-color-on-outline-button-background: var(--sn-role-color-on-primary-container);
|
|
19
|
+
--sn-comp-color-on-text-button-background: var(--sn-role-color-on-primary-container);
|
|
20
|
+
--sn-comp-color-on-elevated-button-background: var(--sn-role-color-on-primary-container);
|
|
21
|
+
--sn-comp-color-on-primary-hover: var(--sn-role-color-on-primary);
|
|
22
|
+
--sn-comp-color-on-disabled-button: var(--sn-role-color-on-disabled);
|
|
23
|
+
--sn-comp-color-on-mark-highlight-background: var(--sn-role-color-on-accent-1-container);
|
|
24
|
+
--sn-comp-color-tonal-button-background: var(--sn-role-color-primary-container);
|
|
25
|
+
--sn-comp-color-outline-button-border: var(--sn-role-color-outline);
|
|
26
|
+
--sn-comp-color-elevated-button-background: var(--sn-role-color-surface-container-high);
|
|
27
|
+
--sn-comp-color-elevated-button-border: var(--sn-role-color-outline);
|
|
28
|
+
--sn-comp-color-elevated-button-shadow: var(--sn-role-color-shadow);
|
|
29
|
+
--sn-comp-color-hover: var(--sn-role-color-primary);
|
|
30
|
+
--sn-comp-color-disabled-button-background: var(--sn-role-color-disabled);
|
|
31
|
+
--sn-comp-color-mark-stroke: var(--sn-role-color-accent-1);
|
|
32
|
+
--sn-comp-color-mark-stroke-on-inverse: var(--sn-role-color-inverse-accent-1);
|
|
33
|
+
--sn-comp-color-mark-highlight-background: var(--sn-role-color-accent-1-container);
|
|
34
|
+
--sn-comp-color-annotation-text: var(--sn-role-color-accent-1);
|
|
35
|
+
--sn-comp-color-stamp-border: var(--sn-role-color-accent-1);
|
|
36
|
+
--sn-comp-color-stamp-text: var(--sn-role-color-accent-1);
|
|
37
|
+
--sn-comp-color-stamp-neutral-border: var(--sn-role-color-on-surface);
|
|
38
|
+
--sn-comp-color-stamp-neutral-text: var(--sn-role-color-on-surface);
|
|
39
|
+
}
|