@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 +0 -0
- package/dist/fonts.css +21 -0
- package/dist/prose.css +156 -0
- package/dist/shadcn.css +206 -0
- package/dist/theme.css +240 -0
- package/dist/tokens/base.json +1043 -0
- package/dist/tokens/surface-ink.json +136 -0
- package/dist/tokens/surface-panel.json +120 -0
- package/dist/tokens.css +289 -0
- package/dist/tokens.json +151 -0
- package/package.json +39 -0
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
|
+
}
|
package/dist/shadcn.css
ADDED
|
@@ -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
|
+
}
|