@cloudgrid-io/ui 0.0.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/dist/.gitkeep ADDED
File without changes
package/dist/fonts.css ADDED
@@ -0,0 +1,21 @@
1
+ /*
2
+ Self-hosted webfonts, not a font CDN: a third-party request in the critical
3
+ path is both a latency cost and a privacy one. Manrope carries body and
4
+ secondary headings; Saira Semi Condensed 800 is the loaded stand-in for the
5
+ licensed Futura Condensed Extra Bold display face (drop the licensed woff2
6
+ into a consumer and the --cg-font-display stack picks it up with no code
7
+ change); JetBrains Mono is used only inside prompt and terminal surfaces.
8
+ The family stacks live in tokens.css.
9
+
10
+ These are bare package specifiers: Vite (Astro) and Next's css-loader both
11
+ resolve them. A pipeline that cannot should import the three fontsource
12
+ packages directly.
13
+
14
+ The three packages are real dependencies of @cloudgrid-io/ui, so installing
15
+ this package is enough. v0 also shipped a '@cloudgrid/ui/fonts' JS entry for
16
+ pipelines that cannot resolve bare CSS specifiers; it was not ported. Add it
17
+ if a consumer turns out to need it rather than on speculation.
18
+ */
19
+ @import '@fontsource-variable/manrope';
20
+ @import '@fontsource/saira-semi-condensed/800.css';
21
+ @import '@fontsource-variable/jetbrains-mono';
package/dist/prose.css ADDED
@@ -0,0 +1,156 @@
1
+ /*
2
+ Prose: typography for HTML you did not write.
3
+
4
+ Everything else in this package styles markup a component controls. This styles
5
+ the output of a markdown parser or a CMS, where the tags arrive as-is and there
6
+ is nowhere to put a class. Hence element selectors under one `.prose` scope
7
+ rather than utilities.
8
+
9
+ Ported from cloudgrid-landing's global.css. It lives here rather than in the
10
+ registry for two reasons. It is plain CSS with no React in it, so the Astro site
11
+ can import it directly, which is the whole point of this package being
12
+ framework-agnostic; and the site already has this recipe, so putting it here
13
+ makes this the owner instead of adding a fourth copy of shared styling to a repo
14
+ that is already tracking a three-copy problem in backlog.md.
15
+
16
+ Load order, same as every other sheet here: tokens.css first, or every var()
17
+ below resolves to nothing.
18
+
19
+ @import "@cloudgrid-io/ui/tokens.css";
20
+ @import "@cloudgrid-io/ui/prose.css";
21
+
22
+ Scoped to `.prose` deliberately. v0's copy also carried a bare
23
+ `:not(pre) > code` rule outside the scope, which restyles every inline code
24
+ element on the page including inside components that already style their own.
25
+ That is left out: a package that reaches outside the class you opted into is a
26
+ package that surprises you.
27
+
28
+ No literal colours, same rule as the rest of the package, and asserted.
29
+ */
30
+
31
+ .prose {
32
+ color: var(--cg-text-secondary);
33
+ }
34
+
35
+ /* The one clamp in v0's recipe, between two ramp steps. Kept as a clamp rather
36
+ than converted to a responsive pair, because plain CSS has no breakpoints to
37
+ hang a pair on and the ramp steps are still the endpoints. */
38
+ .prose h1 {
39
+ font-size: clamp(var(--cg-display-sm), 5vw, var(--cg-display-lg));
40
+ margin-bottom: var(--cg-space-4);
41
+ color: var(--cg-text-primary);
42
+ }
43
+
44
+ .prose h2 {
45
+ font-size: var(--cg-heading-lg);
46
+ margin-block: var(--cg-space-10) var(--cg-space-3);
47
+ color: var(--cg-text-primary);
48
+ }
49
+
50
+ .prose h3 {
51
+ font-size: var(--cg-heading-md);
52
+ margin-block: var(--cg-space-6) var(--cg-space-2);
53
+ color: var(--cg-text-primary);
54
+ }
55
+
56
+ /* The measure sits on the text, not the container, so a wide table or image can
57
+ still use the full width while paragraphs stay readable. */
58
+ .prose p,
59
+ .prose li {
60
+ color: var(--cg-text-secondary);
61
+ max-width: var(--cg-measure);
62
+ overflow-wrap: break-word;
63
+ }
64
+
65
+ .prose p {
66
+ margin-block: var(--cg-space-3);
67
+ }
68
+
69
+ .prose ul,
70
+ .prose ol {
71
+ padding-left: var(--cg-space-5);
72
+ margin-block: var(--cg-space-3);
73
+ }
74
+
75
+ .prose li {
76
+ margin-block: var(--cg-space-1);
77
+ }
78
+
79
+ .prose strong {
80
+ color: var(--cg-text-primary);
81
+ font-weight: var(--cg-weight-bold);
82
+ }
83
+
84
+ .prose pre {
85
+ background: var(--cg-surface-code);
86
+ color: var(--cg-text-on-code);
87
+ border-radius: var(--cg-radius-md);
88
+ padding: var(--cg-space-5) var(--cg-space-6);
89
+ overflow-x: auto;
90
+ margin-block: var(--cg-space-4);
91
+ font-size: var(--cg-mono-size);
92
+ line-height: var(--cg-mono-line);
93
+ }
94
+
95
+ /* Inline code only. `:not(pre)` is what keeps this off code inside a block, which
96
+ would otherwise get a border and a background it already has. */
97
+ .prose :not(pre) > code {
98
+ background: var(--cg-surface-page-alt);
99
+ border: var(--cg-border-width) solid var(--cg-border-hairline);
100
+ border-radius: var(--cg-radius-xs);
101
+ padding: 0.1em 0.35em;
102
+ font-size: 0.9em;
103
+ overflow-wrap: anywhere;
104
+ }
105
+
106
+ .prose blockquote {
107
+ margin-block: var(--cg-space-5);
108
+ padding-left: var(--cg-space-5);
109
+ border-left: var(--cg-border-width-strong) solid var(--cg-border-accent);
110
+ color: var(--cg-text-primary);
111
+ }
112
+
113
+ /* `display: block` with `overflow-x: auto` so a wide table scrolls inside itself
114
+ instead of pushing the page sideways. */
115
+ .prose table {
116
+ border-collapse: collapse;
117
+ margin-block: var(--cg-space-4);
118
+ font-size: var(--cg-body-sm);
119
+ display: block;
120
+ width: 100%;
121
+ max-width: max-content;
122
+ overflow-x: auto;
123
+ }
124
+
125
+ .prose th,
126
+ .prose td {
127
+ border: var(--cg-border-width) solid var(--cg-border-hairline);
128
+ padding: var(--cg-space-2) var(--cg-space-3);
129
+ text-align: left;
130
+ }
131
+
132
+ .prose th {
133
+ background: var(--cg-surface-page-alt);
134
+ font-weight: var(--cg-weight-bold);
135
+ }
136
+
137
+ .prose a {
138
+ color: var(--cg-text-primary);
139
+ text-decoration-thickness: 1px;
140
+ text-underline-offset: 3px;
141
+ }
142
+
143
+ .prose a:hover {
144
+ color: var(--cg-primary);
145
+ }
146
+
147
+ .prose img {
148
+ border-radius: var(--cg-radius-xl);
149
+ margin-block: var(--cg-space-6);
150
+ }
151
+
152
+ .prose hr {
153
+ border: 0;
154
+ border-top: var(--cg-border-width) solid var(--cg-border-hairline);
155
+ margin-block: var(--cg-space-10);
156
+ }
@@ -0,0 +1,206 @@
1
+ /*
2
+ The shadcn bridge: every shadcn semantic variable, resolved to a CloudGrid
3
+ token.
4
+
5
+ This ships from the package rather than living in the registry app, because a
6
+ consumer needs it just as much as we do. `npx shadcn add @cloudgrid/button`
7
+ copies our component source and installs @cloudgrid-io/ui, but the source
8
+ refers to --background, --primary and --border. Without this file those names
9
+ still point at whatever the consumer's own `shadcn init` wrote, which is the
10
+ stock oklch grey palette, and our components render grey in their app while
11
+ looking correct in ours. One file, imported by both, so the two cannot drift.
12
+
13
+ Import order:
14
+
15
+ @import "tailwindcss";
16
+ @import "@cloudgrid-io/ui/fonts.css";
17
+ @import "@cloudgrid-io/ui/tokens.css";
18
+ @import "@cloudgrid-io/ui/theme.css";
19
+ @import "@cloudgrid-io/ui/shadcn.css"; <- after the consumer's own :root
20
+
21
+ Two mappings are worth reading twice, because the obvious one is wrong.
22
+
23
+ --secondary is NOT --cg-secondary. In shadcn, "secondary" is the quiet button
24
+ variant. In this brand --cg-secondary is lime, an accent used sparingly. A lime
25
+ secondary button would put the loudest color in the system on the quietest
26
+ control, so secondary maps to the neutral control fill instead.
27
+
28
+ --accent is NOT an accent either. shadcn uses it for hover and highlight
29
+ backgrounds, so it maps to the subtle surface.
30
+
31
+ THE SELECTOR LIST IS LOAD-BEARING. Declaring these at :root alone is not
32
+ enough, and the failure is invisible until a real component sits on a flipped
33
+ surface.
34
+
35
+ A custom property that references another is substituted where it is DECLARED,
36
+ and descendants inherit the already-resolved value. With
37
+ `--border: var(--cg-border-hairline)` at :root only, --border computes to
38
+ #f1f1f1 at the root and every descendant inherits that literal. Redefining
39
+ --cg-border-hairline inside [data-cg-surface="panel"] then changes nothing:
40
+ measured, --cg-border-hairline was rgba(255,255,255,0.24) inside the panel
41
+ while --border was still #f1f1f1, so every shadcn component kept its
42
+ light-surface borders on purple and on ink.
43
+
44
+ Repeating the block on the flip selectors makes each scope re-resolve the whole
45
+ mapping against its own --cg-* values. Tokens the flips do not touch, like
46
+ --cg-primary, resolve identically in all three, so listing everything costs
47
+ nothing and guarantees nothing is missed.
48
+ */
49
+
50
+ :root,
51
+ [data-cg-surface="panel"],
52
+ [data-cg-surface="ink"] {
53
+ /* surfaces */
54
+ --background: var(--cg-surface-page);
55
+ --card: var(--cg-surface-card);
56
+ --popover: var(--cg-surface-card);
57
+ --muted: var(--cg-surface-subtle);
58
+ --secondary: var(--cg-surface-control);
59
+ --accent: var(--cg-surface-subtle);
60
+
61
+ /* text on those surfaces */
62
+ --foreground: var(--cg-text-primary);
63
+ --card-foreground: var(--cg-text-primary);
64
+ --popover-foreground: var(--cg-text-primary);
65
+ --muted-foreground: var(--cg-text-muted);
66
+ --secondary-foreground: var(--cg-text-primary);
67
+ --accent-foreground: var(--cg-text-primary);
68
+
69
+ /* the brand action color */
70
+ --primary: var(--cg-primary);
71
+ --primary-foreground: var(--cg-text-on-primary);
72
+
73
+ --border: var(--cg-border-hairline);
74
+ --input: var(--cg-border-control);
75
+
76
+ /* The ring is --cg-text-accent, not --cg-focus-ring, and the difference is a
77
+ WCAG failure rather than a preference.
78
+
79
+ --cg-focus-ring is translucent (0.28 on the default surface). Components
80
+ then apply an opacity modifier on top of it, `ring-ring/50`, so the ring
81
+ composites at 0.14 and reaches 1.23:1 against the white page. SC 1.4.11
82
+ requires 3:1 for a non-text indicator. The purple panel was no better:
83
+ white at 0.5 alpha over purple measures 2.60:1. Two of the three surfaces
84
+ failed; only ink passed, and only just, at 3.22:1.
85
+
86
+ No translucent value fixes it while the /50 remains: even a fully opaque
87
+ colour halves to 2.25:1. So the modifier had to go from the components, and
88
+ the ring had to become solid.
89
+
90
+ --cg-text-accent is the token that already flips to the right solid colour
91
+ in every scope, so this needs no change to tokens.css and creates no drift
92
+ against the site and v0 copies of it:
93
+
94
+ default var(--cg-primary) purple 5.84:1 on white
95
+ panel #ffffff white 5.84:1 on purple
96
+ ink var(--cg-secondary) lime 10.33:1 on ink
97
+
98
+ --cg-focus-ring is untouched and still backs the brand's own
99
+ --cg-ring-focus shadow. Pinned by an assertion in
100
+ registry/e2e/token-wiring.spec.ts. */
101
+ --ring: var(--cg-text-accent);
102
+
103
+ /* charts: the brand trio first, then neutrals, so a three-series chart is
104
+ purple, lime, cyan and a five-series one degrades to grey rather than
105
+ inventing colors. */
106
+ --chart-1: var(--cg-primary);
107
+ --chart-2: var(--cg-secondary);
108
+ --chart-3: var(--cg-tertiary);
109
+ --chart-4: var(--cg-neutral-500);
110
+ --chart-5: var(--cg-neutral-700);
111
+
112
+ /* sidebar: a subtle wash, not its own palette */
113
+ --sidebar: var(--cg-surface-page-alt);
114
+ --sidebar-foreground: var(--cg-text-primary);
115
+ --sidebar-primary: var(--cg-primary);
116
+ --sidebar-primary-foreground: var(--cg-text-on-primary);
117
+ --sidebar-accent: var(--cg-surface-subtle);
118
+ --sidebar-accent-foreground: var(--cg-text-primary);
119
+ --sidebar-border: var(--cg-border-hairline);
120
+ --sidebar-ring: var(--cg-focus-ring);
121
+
122
+ /* shadcn derives its own radius scale from this one value. Pointing it at
123
+ --cg-radius-lg keeps anything not explicitly mapped in the right family. */
124
+ --radius: var(--cg-radius-lg);
125
+
126
+ /* Closed 2026-08-16 (#62). This was the package's one literal colour, a
127
+ #c0392b placeholder, because the brand had no red of any kind. It now points
128
+ at --cg-danger like every other mapping here.
129
+
130
+ This declaration sits inside the combined :root / panel / ink selector, which
131
+ is what makes it flip: per rule 2 a custom property referencing another is
132
+ substituted where it is DECLARED, so being declared once per scope is what
133
+ gives an invalid field the on-purple red inside a panel rather than the
134
+ white-surface one. Declared at :root alone it would freeze to #E53935
135
+ everywhere, which measures 1.38:1 on the panel. */
136
+ --destructive: var(--cg-danger);
137
+ }
138
+ /* The primary action on purple. Operator decision, 2026-08-12, issue #41.
139
+
140
+ --cg-primary IS the panel surface: --cg-surface-panel is var(--cg-primary).
141
+ So mapping --primary to it inside the panel scope gave a button the exact
142
+ colour of the ground behind it. Measured before the fix: button background
143
+ rgb(90,71,240), panel background rgb(90,71,240). Contrast against the surface
144
+ 1:1. The label stayed white so the text was readable, but the control had no
145
+ shape at all.
146
+
147
+ Lime is the brand's other action colour and the natural "go" on purple, so
148
+ that is what a primary becomes here, with ink on top rather than white.
149
+
150
+ Both flipped surfaces, not just the panel. I first wrote this for the panel
151
+ only, on the assumption that purple keeps its shape against ink. The assertion
152
+ said otherwise: purple on #222 measures 2.72:1, also under the 3:1 floor. So
153
+ the reported bug was two of three surfaces and only one had been noticed.
154
+
155
+ The rule that falls out is simple enough to hold in your head: purple on white,
156
+ lime on either dark brand surface. Lime on ink measures about 10:1.
157
+
158
+ Guarded by an assertion that measures the button against the surface BEHIND it
159
+ in all three scopes. The suite already had two tests here and neither could see
160
+ this: one asserts the button renders the brand colour in every scope, which is
161
+ satisfied by exactly the behaviour that caused the bug, and the other compares
162
+ the label against the button rather than the button against its ground. */
163
+ [data-cg-surface="panel"],
164
+ [data-cg-surface="ink"] {
165
+ --primary: var(--cg-secondary);
166
+ --primary-foreground: var(--cg-text-on-accent);
167
+ }
168
+
169
+
170
+ /*
171
+ Backgrounds that do not flip on their own.
172
+
173
+ Every shadcn *foreground* maps to a token the surface blocks redefine, so text
174
+ turns white inside a panel automatically. Four backgrounds do not, because the
175
+ brand tokens behind them describe the page rather than a container:
176
+
177
+ --background -> --cg-surface-page static
178
+ --muted -> --cg-surface-subtle static
179
+ --accent -> --cg-surface-subtle static
180
+ --secondary -> --cg-surface-control static
181
+
182
+ Half of each pair follows the surface and half does not, so they collide
183
+ exactly where the surface changes. An outline button on a panel rendered white
184
+ text on a white pill: 1:1 contrast, the label completely invisible, and
185
+ nothing errored. Same shape for bg-muted, bg-secondary and bg-accent.
186
+
187
+ So each scope gets the four named explicitly, using tokens that exist in that
188
+ scope. "Background" inside a container means that container, not the page.
189
+
190
+ The exact choices below are a design call as much as a fix, and worth a
191
+ reviewer's eye: on the panel these resolve to transparent, so the purple shows
192
+ through, which is what an outline control should do anyway.
193
+ */
194
+ [data-cg-surface="panel"] {
195
+ --background: var(--cg-surface-panel);
196
+ --muted: var(--cg-surface-card);
197
+ --accent: var(--cg-surface-card);
198
+ --secondary: var(--cg-surface-card);
199
+ }
200
+
201
+ [data-cg-surface="ink"] {
202
+ --background: var(--cg-surface-code);
203
+ --muted: var(--cg-surface-page-alt);
204
+ --accent: var(--cg-surface-page-alt);
205
+ --secondary: var(--cg-surface-page-alt);
206
+ }
package/dist/theme.css ADDED
@@ -0,0 +1,240 @@
1
+ /*
2
+ Tailwind v4 theme for CloudGrid.
3
+
4
+ This maps Tailwind's utility namespaces onto the --cg-* custom properties in
5
+ tokens.css. It does not define any values of its own: every entry is a var()
6
+ reference, so a theme change stays a one-file edit in tokens.css and never has
7
+ to be mirrored here.
8
+
9
+ Load order matters. tokens.css must be imported before this file, or the
10
+ var() references resolve to nothing:
11
+
12
+ @import "tailwindcss";
13
+ @import "@cloudgrid-io/ui/tokens.css";
14
+ @import "@cloudgrid-io/ui/theme.css";
15
+
16
+ The setup guide's Step 2 example mapped --color-background, --color-accent and
17
+ friends. Those names do not exist in this brand system, so this file is
18
+ authored against the real tokens rather than copied from the guide.
19
+
20
+ Utilities are a convenience layer, not the contract. The canon in tokens.css
21
+ stands: components consume custom properties exclusively and must never
22
+ hardcode a hex. Where a component needs a token with no utility (durations,
23
+ transitions, the press scale), use var(--cg-*) directly.
24
+ */
25
+
26
+ /*
27
+ `inline` is load-bearing, not stylistic. Do not remove it.
28
+
29
+ With a plain `@theme`, Tailwind evaluates each var() once, at :root, and emits
30
+ the resolved literal. Utilities then point at the theme variable, which is
31
+ frozen to whatever the default scope resolved to. The surface flips stop
32
+ working: inside data-cg-surface="panel", --cg-border-hairline correctly becomes
33
+ rgba(255,255,255,0.24), but border-hairline still paints #f1f1f1, because the
34
+ utility never sees the flipped variable.
35
+
36
+ With `inline`, Tailwind inlines the theme value into the utility, so
37
+ border-hairline compiles to `border-color: var(--cg-border-hairline)` and the
38
+ variable resolves at the element. The flip works.
39
+
40
+ Measured, not assumed: with plain @theme, --color-hairline computed to #f1f1f1
41
+ in all three scopes while --cg-border-hairline flipped correctly in each.
42
+
43
+ One consequence to know: text does not flip by inheritance. A container that
44
+ sets data-cg-surface must also apply a text color from a token (text-ink), or
45
+ its children inherit the already-resolved color from an ancestor and stay dark.
46
+ */
47
+ @theme inline {
48
+ /* ---- names this file must not claim ----
49
+ shadcn's own theme layer owns background, foreground, card, popover,
50
+ primary, secondary, muted, accent, destructive, border, input, ring,
51
+ chart-* and sidebar-*, and its component source depends on them. Whichever
52
+ block loads last wins, so claiming one of those names here does not produce
53
+ an error: it silently changes what a shadcn component renders.
54
+
55
+ Found the hard way. An earlier version of this file mapped --color-muted to
56
+ the muted *text* color and --color-accent to the accent *text* color. In
57
+ shadcn both are background colors, so text-muted and text-accent rendered
58
+ near-white on white. The badge was invisible.
59
+
60
+ So: muted text is --color-faint, the brand purple is --color-brand, and the
61
+ brand trio is brand / lime / cyan. Nothing below overlaps shadcn. */
62
+
63
+ /* ---- surfaces ----
64
+ One name serves every color utility, so --color-page gives bg-page,
65
+ text-page and border-page alike. */
66
+ --color-page: var(--cg-surface-page);
67
+ --color-page-alt: var(--cg-surface-page-alt);
68
+ --color-subtle: var(--cg-surface-subtle);
69
+ --color-control: var(--cg-surface-control);
70
+ --color-panel: var(--cg-surface-panel);
71
+ --color-code: var(--cg-surface-code);
72
+
73
+ /* ---- text ----
74
+ Deliberately not called --color-primary: in this system "primary" is the
75
+ purple brand color, while the primary *text* color is ink. Collapsing both
76
+ onto text-primary would make the utility lie. */
77
+ --color-ink: var(--cg-text-primary);
78
+ --color-body: var(--cg-text-secondary);
79
+ --color-faint: var(--cg-text-muted);
80
+ --color-placeholder: var(--cg-text-placeholder);
81
+ --color-inverse: var(--cg-text-inverse);
82
+ --color-on-primary: var(--cg-text-on-primary);
83
+ --color-on-accent: var(--cg-text-on-accent);
84
+ --color-on-code: var(--cg-text-on-code);
85
+
86
+ /* ---- brand ---- */
87
+ --color-brand: var(--cg-primary);
88
+ --color-brand-press: var(--cg-primary-press);
89
+ --color-brand-tint: var(--cg-primary-tint);
90
+ --color-lime: var(--cg-secondary);
91
+ --color-lime-press: var(--cg-secondary-press);
92
+ --color-cyan: var(--cg-tertiary);
93
+ --color-cyan-press: var(--cg-tertiary-press);
94
+ /* `danger`, not `destructive`: rule 4 says never claim a name shadcn's theme
95
+ layer owns, and `destructive` is one of its own. This is the CloudGrid name
96
+ for the same value, alongside faint / brand / lime / cyan. shadcn.css bridges
97
+ --destructive to the same token, so both spellings resolve to one source. */
98
+ --color-danger: var(--cg-danger);
99
+
100
+ /* ---- lines ---- */
101
+ --color-hairline: var(--cg-border-hairline);
102
+ --color-line: var(--cg-border-control);
103
+ --color-line-strong: var(--cg-border-strong);
104
+ --color-line-accent: var(--cg-border-accent);
105
+
106
+ /* ---- type families ----
107
+ --font-sans points at the body face so the stock font-sans utility is
108
+ correct rather than something to remember not to use. */
109
+ --font-sans: var(--cg-font-body);
110
+ --font-body: var(--cg-font-body);
111
+ --font-display: var(--cg-font-display);
112
+ --font-mono: var(--cg-font-mono);
113
+
114
+ /* ---- type scale ----
115
+ Every --text-* carries a --text-*--line-height companion, because Tailwind
116
+ emits the pair together:
117
+
118
+ .text-body-sm { font-size: var(--cg-body-sm);
119
+ line-height: var(--tw-leading, var(--text-body-sm--line-height)) }
120
+
121
+ Without the companion the utility sets a size and no leading, and the
122
+ leading falls back to whatever an ancestor happens to declare. That reads
123
+ fine here, where globals.css sets line-height on body, and wrong in a
124
+ consumer app that copied the component and not the stylesheet -- which is
125
+ the whole population this registry serves. A `leading-*` utility still wins
126
+ when a component wants to override, via --tw-leading.
127
+
128
+ Two other companions Tailwind supports, added only where the brand actually
129
+ pairs a value with a size rather than everywhere it is possible:
130
+ --letter-spacing on the display ramp, because the brand specifies
131
+ --cg-display-tracking for it, and --font-weight on the label, because
132
+ --cg-label-weight exists for exactly that. Body and heading have no specified
133
+ tracking or weight, so they get none: a companion that invents a value is
134
+ worse than an absent one, since it forces a weight where a component expected
135
+ to inherit. Closes the gap noted in notes.md after #30. */
136
+ --text-display-xl: var(--cg-display-xl);
137
+ --text-display-xl--line-height: var(--cg-display-line);
138
+ --text-display-xl--letter-spacing: var(--cg-display-tracking);
139
+ --text-display-lg: var(--cg-display-lg);
140
+ --text-display-lg--line-height: var(--cg-display-line);
141
+ --text-display-lg--letter-spacing: var(--cg-display-tracking);
142
+ --text-display-md: var(--cg-display-md);
143
+ --text-display-md--line-height: var(--cg-display-line);
144
+ --text-display-md--letter-spacing: var(--cg-display-tracking);
145
+ --text-display-sm: var(--cg-display-sm);
146
+ --text-display-sm--line-height: var(--cg-display-line);
147
+ --text-display-sm--letter-spacing: var(--cg-display-tracking);
148
+ --text-heading-lg: var(--cg-heading-lg);
149
+ --text-heading-lg--line-height: var(--cg-heading-line);
150
+ --text-heading-md: var(--cg-heading-md);
151
+ --text-heading-md--line-height: var(--cg-heading-line);
152
+ --text-heading-sm: var(--cg-heading-sm);
153
+ --text-heading-sm--line-height: var(--cg-heading-line);
154
+ --text-body-lg: var(--cg-body-lg);
155
+ --text-body-lg--line-height: var(--cg-body-line);
156
+ --text-body-md: var(--cg-body-md);
157
+ --text-body-md--line-height: var(--cg-body-line);
158
+ --text-body-sm: var(--cg-body-sm);
159
+ --text-body-sm--line-height: var(--cg-body-line);
160
+ --text-caption: var(--cg-caption);
161
+ --text-caption--line-height: var(--cg-body-line);
162
+ --text-mono: var(--cg-mono-size);
163
+ --text-mono--line-height: var(--cg-mono-line);
164
+ --text-label: var(--cg-label-size);
165
+ --text-label--line-height: var(--cg-body-line);
166
+ --text-label--font-weight: var(--cg-label-weight);
167
+
168
+ /* ---- weights ----
169
+ `--font-weight-display` is unreachable and kept only for completeness. Both
170
+ namespaces build the same utility name: the family `--font-display` and the
171
+ weight `--font-weight-display` each want `font-display`, and the family wins.
172
+ Measured, not assumed: the build emits
173
+ `.font-display,.font-heading{font-family:var(--cg-font-display)}` and no
174
+ rule anywhere sets font-weight 800.
175
+
176
+ That is rule 4 one layer in. Rule 4 is about not claiming a name shadcn
177
+ owns; this is two of our own entries claiming one name, with the same
178
+ silent outcome.
179
+
180
+ So the brand's display weight is exposed as `font-heavy`. Different word,
181
+ same token, and reachable. Needed by any display-face heading that is not an
182
+ h1, since globals.css only styles h1. */
183
+ --font-weight-regular: var(--cg-weight-regular);
184
+ --font-weight-medium: var(--cg-weight-medium);
185
+ --font-weight-bold: var(--cg-weight-bold);
186
+ --font-weight-display: var(--cg-weight-display);
187
+ --font-weight-heavy: var(--cg-weight-display);
188
+
189
+ /* ---- leading and tracking ---- */
190
+ --leading-display: var(--cg-display-line);
191
+ --leading-heading: var(--cg-heading-line);
192
+ --leading-body: var(--cg-body-line);
193
+ --leading-mono: var(--cg-mono-line);
194
+ --tracking-display: var(--cg-display-tracking);
195
+ --tracking-body: var(--cg-body-tracking);
196
+
197
+ /* ---- corners ---- */
198
+ --radius-xs: var(--cg-radius-xs);
199
+ --radius-sm: var(--cg-radius-sm);
200
+ --radius-md: var(--cg-radius-md);
201
+ --radius-lg: var(--cg-radius-lg);
202
+ --radius-xl: var(--cg-radius-xl);
203
+ --radius-2xl: var(--cg-radius-2xl);
204
+ --radius-pill: var(--cg-radius-pill);
205
+
206
+ /* ---- elevation ----
207
+ Hairlines first. Shadows appear on the wash or on hover, never as the
208
+ default way to contain something. */
209
+ --shadow-card: var(--cg-shadow-card);
210
+ --shadow-raised: var(--cg-shadow-raised);
211
+ --shadow-hover: var(--cg-shadow-hover);
212
+ --shadow-panel: var(--cg-shadow-panel);
213
+ --shadow-focus: var(--cg-ring-focus);
214
+
215
+ /* ---- spacing ----
216
+ --spacing sets the 4px base, so the numeric utilities (p-4, gap-6) line up
217
+ with the --cg-space-* ramp by construction. The named entries below are the
218
+ layout measurements, which have no numeric equivalent. */
219
+ --spacing: 4px;
220
+ --spacing-page-pad: var(--cg-page-pad);
221
+ --spacing-stack-tight: var(--cg-stack-tight);
222
+ --spacing-stack: var(--cg-stack);
223
+ --spacing-stack-loose: var(--cg-stack-loose);
224
+ --spacing-section-gap: var(--cg-section-gap);
225
+ --spacing-header: var(--cg-header-height);
226
+ --spacing-footer: var(--cg-footer-height);
227
+
228
+ /* ---- widths ---- */
229
+ --container-form: var(--cg-column-form);
230
+ --container-wide: var(--cg-column-wide);
231
+ --container-page: var(--cg-container);
232
+ --container-measure: var(--cg-measure);
233
+
234
+ /* ---- motion ----
235
+ Tailwind has an --ease-* namespace, so these become ease-out / ease-in-out.
236
+ Durations have no theme namespace in v4: use var(--cg-duration) directly, or
237
+ the duration-[…] arbitrary value. */
238
+ --ease-out: var(--cg-ease-out);
239
+ --ease-in-out: var(--cg-ease-in-out);
240
+ }