@half-built/css 0.1.0 → 0.3.0

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 CHANGED
@@ -9,3 +9,83 @@ Extracted from the private `half-built-robots-blog` repository, where
9
9
  this CSS was built and used in production. The extraction review that
10
10
  checked it for blog-specific assumptions before the move is the design
11
11
  record for this package.
12
+
13
+ ## Usage
14
+
15
+ Import order matters. In your base layout, load the layer declaration
16
+ first, then tokens, then the base entry, then the patterns you use:
17
+
18
+ ```js
19
+ import "@half-built/css/layers";
20
+ import "@half-built/css/tokens";
21
+ import "@half-built/css";
22
+ import "@half-built/css/patterns.css";
23
+ import "@half-built/css/prose.css";
24
+ import "@half-built/css/code.css";
25
+ ```
26
+
27
+ The island stylesheets (`lightbox.css`, `plate-modal.css`,
28
+ `path-player.css`) load the same way, from whichever layout or
29
+ component mounts their island. The `.` entry is the base layer (reset,
30
+ shell, link-tip, scroll-top); it is not the whole system, which is why
31
+ the explicit order above exists.
32
+
33
+ The breakpoints are `@custom-media` rules, so your build needs
34
+ `postcss-custom-media` with `@csstools/postcss-global-data` fed the
35
+ breakpoints file:
36
+
37
+ ```js
38
+ // postcss.config.mjs
39
+ import { fileURLToPath } from "node:url";
40
+ const breakpoints = fileURLToPath(
41
+ import.meta.resolve("@half-built/css/tokens/breakpoints.css"),
42
+ );
43
+ // plugins: [postcssGlobalData({ files: [breakpoints] }), postcssCustomMedia()]
44
+ ```
45
+
46
+ Sites add their own `site` cascade layer after these imports for local
47
+ overrides. The layer order in `layers.css` already declares it.
48
+
49
+ ## Theming
50
+
51
+ The entire third-party theming surface is six primitives: two brand
52
+ families, three stops each. Override any or all of them in your
53
+ `site` layer; every other token in the system derives from these six.
54
+
55
+ | Variable | Default | Role |
56
+ |---|---|---|
57
+ | `--brand-1-500` | `#ffaa3c` | Base: lines and fills, dark-theme text ink, light-theme focus ring. |
58
+ | `--brand-1-600` | `#d1820f` | Chart kin; light-theme track-x. |
59
+ | `--brand-1-700` | `#a36300` | Text ink on the light paper. |
60
+ | `--brand-2-300` | `#8ee6f2` | Light kin: dark-theme text ink and track-y. |
61
+ | `--brand-2-500` | `#3cc7dd` | Base: lines and fills, dark-theme focus ring. |
62
+ | `--brand-2-700` | `#1a7f90` | Text ink on the light paper. |
63
+
64
+ An override looks like this:
65
+
66
+ ```css
67
+ /* half-built palette override, generated at ui.half-built-robots.com */
68
+ :root {
69
+ --brand-1-500: #3cc74a;
70
+ --brand-1-600: #2a9c38;
71
+ --brand-1-700: #1c6b27;
72
+ --brand-2-300: #a0e8b0;
73
+ --brand-2-500: #34b350;
74
+ --brand-2-700: #1f7a38;
75
+ }
76
+ ```
77
+
78
+ A derived ramp is built to three thresholds: the 700 stops clear 4.5:1
79
+ against white, the 300 stop clears 4.5:1 against `#111111`, and the
80
+ 600 stop clears 3:1 against white. The shipped defaults clear all
81
+ three (the 600 stop was retuned to `#d1820f` for the 3:1 non-text
82
+ gate in 0.2.0). These are targets the derivation satisfies, not a
83
+ floor the system enforces on your behalf. If
84
+ you write your own six values instead of deriving them, you own their
85
+ contrast.
86
+
87
+ The reference site generates a compliant block for you: give it two
88
+ base colors and its palette editor derives the other four stops and
89
+ prints a ready-to-paste override in exactly the shape above. It will
90
+ live at `ui.half-built-robots.com` once deployed; today the source for
91
+ that derivation is `site/src/lib/derive-palette.ts` in this repo.
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@half-built/css",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Design tokens, base, patterns, and prose CSS for the half-built design system.",
5
5
  "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/curthenrichs/half-built-ui.git",
9
+ "directory": "packages/css"
10
+ },
6
11
  "type": "module",
7
12
  "files": ["src"],
8
13
  "dependencies": {
@@ -12,6 +17,7 @@
12
17
  ".": "./src/global.css",
13
18
  "./layers": "./src/layers.css",
14
19
  "./tokens": "./src/tokens.css",
20
+ "./package.json": "./package.json",
15
21
  "./*": "./src/*"
16
22
  },
17
23
  "publishConfig": { "access": "public" }
@@ -36,27 +36,49 @@
36
36
  --gray-750: #3a3a3a;
37
37
  --gray-400: #9a9a9a;
38
38
 
39
- /* Accents. amber-500 is the shared accent across the ecosystem;
40
- amber-700 is its legible kin for text, tuned here for this site
41
- the way cyan-700 was: the amber hue at full saturation, taken
42
- down until it clears WCAG AA (4.5:1) as body text on the white
43
- surface (4.84) and on the accent's 6% tint (4.66). It replaced
44
- #b5680c, a value borrowed from BEADZ that sat at 4.24:1 on white
45
- (owner call 2026-09-03; BEADZ is not a source of values). */
46
- --amber-500: #ffaa3c;
47
- --amber-700: #a36300;
48
- /* Second accent, the cyan family (owner request 2026-08-26): chosen
49
- to sit where the amber sits, cyan-500 matches amber-500's contrast
50
- on both grounds (line and fill, not text on white) and cyan-700
51
- is its legible kin for text on the light ground. */
52
- --cyan-300: #8ee6f2;
53
- --cyan-500: #3cc7dd;
54
- --cyan-700: #1a7f90;
39
+ /* Brand ramps, the entire third-party theming surface. Family 1 is
40
+ the ecosystem amber by default, family 2 its cyan complement; the
41
+ names are hue-neutral because a consumer may override all six
42
+ (spec: docs/superpowers/specs/2026-09-05-reference-site-design.md).
43
+ 500 is the base, 700 its AA text ink on the light paper, 600 the
44
+ chart kin, 300 the light kin legible as text on the dark ground.
45
+ Brand-1-700 is tuned here for this site the way brand-2-700 was:
46
+ the amber hue at full saturation, taken down until it clears WCAG
47
+ AA (4.5:1) as body text on the white surface (4.84) and on the
48
+ accent's 6% tint (4.66). It replaced #b5680c, a value borrowed
49
+ from BEADZ that sat at 4.24:1 on white (owner call 2026-09-03;
50
+ BEADZ is not a source of values). Family 2 (owner request
51
+ 2026-08-26) was chosen to sit where family 1 sits: brand-2-500
52
+ matches brand-1-500's contrast on both grounds (line and fill,
53
+ not text on white) and brand-2-700 is its legible kin for text on
54
+ the light ground. Brand-1-600 was retuned 2026-09-05 (owner call)
55
+ from #d98a1f, which sat at 2.76:1 on white: walked down in OKLCH
56
+ with hue and chroma held until it clears 3:1 as a non-text chart
57
+ line on the light paper (now 3.04). */
58
+ --brand-1-500: #ffaa3c;
59
+ --brand-1-600: #d1820f;
60
+ --brand-1-700: #a36300;
61
+ --brand-2-300: #8ee6f2;
62
+ --brand-2-500: #3cc7dd;
63
+ --brand-2-700: #1a7f90;
64
+ /* Code-viewer kin of family 1 (spec 2026-09-06, live code colors):
65
+ the values the Shiki theme has always baked, promoted to
66
+ primitives so the code token roles below them can follow a
67
+ palette override. 300 is a true lightness stop (OKLCH L 0.885,
68
+ above the 500's 0.804), the family's light kin the way
69
+ brand-2-300 is. "vivid" is deliberately not a numbered stop: at
70
+ L 0.684 it sits at the 600's lightness with more chroma
71
+ (0.159 vs 0.146), so a ramp number would misstate what it is. */
72
+ --brand-1-300: #ffd18a;
73
+ --brand-1-vivid: #e07c14;
55
74
  --red-800: #7a1f16;
56
75
  --red-300: #ff8a7a;
57
- --ochre-600: #d98a1f;
58
76
  --violet-600: #7a5aa0;
59
77
  --violet-300: #b39ad6;
78
+ /* Fixed system color like red and violet above, so it keeps a hue
79
+ name: the code comment ink, a dim warm gray that stays put when
80
+ the brand families are overridden. */
81
+ --umber-500: #8a7a63;
60
82
 
61
83
  /* Strokes, spacing, elevation, motion */
62
84
  --stroke-1: 1px;
@@ -23,10 +23,10 @@
23
23
 
24
24
  /* The bright amber is legible as text on the dark ground, so the
25
25
  accent and its ink form collapse to one value here. */
26
- --accent-1: var(--amber-500);
27
- --accent-1-ink: var(--amber-500);
28
- --accent-2: var(--cyan-500);
29
- --accent-2-ink: var(--cyan-300);
26
+ --accent-1: var(--brand-1-500);
27
+ --accent-1-ink: var(--brand-1-500);
28
+ --accent-2: var(--brand-2-500);
29
+ --accent-2-ink: var(--brand-2-300);
30
30
  --on-accent-1: var(--black-900);
31
31
  --on-accent-2: var(--black-900);
32
32
  --error-ink: var(--red-300);
@@ -36,15 +36,23 @@
36
36
  --code-bg: var(--ink-900);
37
37
  --code-fg: var(--parchment);
38
38
  --code-line: var(--ink-800);
39
+ /* Syntax token roles (spec 2026-09-06): what the Shiki theme bakes
40
+ as hex, restated as variables so the code-vars transformer can
41
+ emit tokens that follow a palette override. Identical in both
42
+ themes because the code surface is always the warm dark block. */
43
+ --code-token-keyword: var(--brand-1-500);
44
+ --code-token-function: var(--brand-1-300);
45
+ --code-token-string: var(--brand-1-vivid);
46
+ --code-token-comment: var(--umber-500);
39
47
 
40
48
  --veil: var(--black-a60);
41
49
  --mat-line: var(--gray-800);
42
50
  /* Keyboard ring: the second accent by night, the first by day
43
51
  (owner call 2026-08-26). */
44
- --focus-ring: var(--cyan-500);
52
+ --focus-ring: var(--brand-2-500);
45
53
 
46
- --track-x: var(--amber-500);
47
- --track-y: var(--cyan-300);
54
+ --track-x: var(--brand-1-500);
55
+ --track-y: var(--brand-2-300);
48
56
  --track-z: var(--violet-300);
49
57
 
50
58
  /* The subscribe panel inverts by night: a dark panel with an amber
@@ -53,8 +61,8 @@
53
61
  2026-08-26). */
54
62
  --panel-bg: var(--black-850);
55
63
  --panel-ink: var(--parchment);
56
- --panel-rule: var(--amber-500);
64
+ --panel-rule: var(--brand-1-500);
57
65
  --panel-error: var(--red-300);
58
- --panel-ring: var(--amber-500);
66
+ --panel-ring: var(--brand-1-500);
59
67
  }
60
68
  }
@@ -21,13 +21,13 @@
21
21
  /* Amber survives inside the code island and as a line or fill
22
22
  color; --accent-1-ink is its legible kin for any text on the
23
23
  surface (the tagline's box, Callout tip and warning types). */
24
- --accent-1: var(--amber-500);
25
- --accent-1-ink: var(--amber-700);
24
+ --accent-1: var(--brand-1-500);
25
+ --accent-1-ink: var(--brand-1-700);
26
26
  /* Second accent (cyan), the amber's complement. Same split: the
27
27
  bright value is a line or fill, the dark one is text. No component
28
28
  reads it yet; its jobs are an owner decision (2026-08-26). */
29
- --accent-2: var(--cyan-500);
30
- --accent-2-ink: var(--cyan-700);
29
+ --accent-2: var(--brand-2-500);
30
+ --accent-2-ink: var(--brand-2-700);
31
31
  /* Text on an accent fill (a hovered or pressed accent button). */
32
32
  --on-accent-1: var(--ink-900);
33
33
  --on-accent-2: var(--ink-900);
@@ -37,6 +37,14 @@
37
37
  --code-bg: var(--ink-900);
38
38
  --code-fg: var(--parchment);
39
39
  --code-line: var(--ink-800);
40
+ /* Syntax token roles (spec 2026-09-06): what the Shiki theme bakes
41
+ as hex, restated as variables so the code-vars transformer can
42
+ emit tokens that follow a palette override. Identical in both
43
+ themes because the code surface is always the warm dark block. */
44
+ --code-token-keyword: var(--brand-1-500);
45
+ --code-token-function: var(--brand-1-300);
46
+ --code-token-string: var(--brand-1-vivid);
47
+ --code-token-comment: var(--umber-500);
40
48
 
41
49
  /* Plate-modal chrome (spec 2026-07-30): veil over the blurred page,
42
50
  and the viewbox mat one step lighter than --hairline. Values
@@ -45,16 +53,16 @@
45
53
  --mat-line: var(--gray-075);
46
54
 
47
55
  /* Path-player timeline series (spec 2026-08-22, owner question 1). */
48
- --track-x: var(--ochre-600);
56
+ --track-x: var(--brand-1-600);
49
57
  /* The second data track is the second accent (owner call 2026-08-26). */
50
- --track-y: var(--cyan-500);
58
+ --track-y: var(--brand-2-500);
51
59
  --track-z: var(--violet-600);
52
60
 
53
61
  /* The subscribe panel, the amber block that bridges every property
54
62
  back to the blog. By day: amber ground, warm-dark ink and rule, a
55
63
  dark button with white text. By night the panel inverts (see
56
64
  theme-dark.css) so the button stays the brightest thing in it. */
57
- --panel-bg: var(--amber-500);
65
+ --panel-bg: var(--brand-1-500);
58
66
  --panel-ink: var(--ink-900);
59
67
  --panel-rule: var(--ink-900);
60
68
  --panel-error: var(--red-800);
@@ -66,7 +74,7 @@
66
74
  so it reads on both grounds and never means the same as hover. */
67
75
  /* Keyboard ring: the first accent by day, the second by night
68
76
  (owner call 2026-08-26). */
69
- --focus-ring: var(--amber-500);
77
+ --focus-ring: var(--brand-1-500);
70
78
 
71
79
  /* The signature stroke: 2px here, 3px on BEADZ. */
72
80
  --stroke: var(--stroke-2);