@robomous/ui-core 0.1.0 → 0.2.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 +19 -7
- package/dist/{primitives → components}/alert.js +1 -1
- package/dist/{primitives → components}/badge.js +1 -1
- package/dist/{primitives → components}/button.d.ts +1 -1
- package/dist/{primitives → components}/button.js +4 -1
- package/dist/{primitives → components}/card.d.ts +1 -1
- package/dist/{primitives → components}/card.js +2 -2
- package/dist/{primitives → components}/combobox.js +4 -4
- package/dist/{primitives → components}/dialog.js +1 -1
- package/dist/{primitives → components}/dropdown-menu.js +12 -7
- package/dist/{primitives → components}/field.js +2 -4
- package/dist/{primitives → components}/input-group.js +1 -1
- package/dist/{primitives → components}/input.js +1 -1
- package/dist/{primitives → components}/label.js +1 -1
- package/dist/components/progress.js +7 -0
- package/dist/{primitives → components}/select.d.ts +3 -1
- package/dist/{primitives → components}/select.js +7 -4
- package/dist/{primitives → components}/separator.js +1 -1
- package/dist/{primitives → components}/sheet.js +1 -1
- package/dist/{primitives → components}/skeleton.js +1 -1
- package/dist/{primitives → components}/sonner.js +17 -12
- package/dist/{primitives → components}/table.d.ts +1 -1
- package/dist/{primitives → components}/table.js +3 -3
- package/dist/{primitives → components}/tabs.js +1 -1
- package/dist/{primitives → components}/textarea.js +1 -1
- package/dist/{primitives → components}/tooltip.js +1 -1
- package/dist/gates/index.d.ts +28 -0
- package/dist/gates/index.js +142 -0
- package/dist/index.d.ts +31 -32
- package/dist/index.js +32 -33
- package/dist/{tokens.d.ts → theme/tokens.d.ts} +5 -5
- package/dist/{tokens.js → theme/tokens.js} +12 -12
- package/package.json +23 -12
- package/{shadcn → src/components}/alert.tsx +14 -21
- package/src/{primitives → components}/badge.tsx +15 -21
- package/src/{primitives → components}/button.tsx +13 -10
- package/{shadcn → src/components}/card.tsx +16 -31
- package/src/{primitives → components}/combobox.test.tsx +15 -2
- package/src/{primitives → components}/combobox.tsx +45 -74
- package/src/{primitives/primitives.test.tsx → components/components.test.tsx} +106 -69
- package/src/{primitives → components}/dialog.tsx +31 -54
- package/src/{primitives → components}/dropdown-menu.tsx +51 -65
- package/src/{primitives → components}/field.tsx +48 -62
- package/src/{primitives → components}/input-group.tsx +37 -49
- package/{shadcn → src/components}/input.tsx +5 -5
- package/{shadcn → src/components}/label.tsx +7 -10
- package/src/{primitives → components}/progress.tsx +7 -6
- package/{shadcn → src/components}/select.tsx +40 -40
- package/{shadcn → src/components}/separator.tsx +7 -7
- package/src/{primitives → components}/sheet.tsx +27 -44
- package/{shadcn → src/components}/skeleton.tsx +3 -3
- package/src/{primitives → components}/sonner.test.tsx +12 -4
- package/src/components/sonner.tsx +67 -0
- package/{shadcn → src/components}/table.tsx +17 -44
- package/{shadcn → src/components}/tabs.tsx +16 -26
- package/{shadcn → src/components}/textarea.tsx +5 -5
- package/{shadcn → src/components}/tooltip.tsx +12 -16
- package/src/gates/design.test.ts +188 -0
- package/src/gates/index.test.ts +11 -0
- package/src/gates/index.ts +165 -0
- package/src/gates/tokens.test.ts +215 -0
- package/src/harness.test.tsx +1 -1
- package/src/index.ts +63 -33
- package/src/theme/imports.test.ts +160 -0
- package/src/{statusTone.test.ts → theme/statusTone.test.ts} +5 -1
- package/src/{styles.css → theme/styles.css} +45 -31
- package/src/theme/tailwind.css +646 -0
- package/src/{tokens.test.ts → theme/tokens.test.ts} +30 -23
- package/src/{tokens.ts → theme/tokens.ts} +12 -12
- package/components.json +0 -13
- package/dist/lib/button.d.ts +0 -1
- package/dist/lib/button.js +0 -4
- package/dist/lib/cn.d.ts +0 -18
- package/dist/lib/cn.js +0 -21
- package/dist/lib/menu.d.ts +0 -1
- package/dist/lib/menu.js +0 -16
- package/dist/lib/progress.d.ts +0 -3
- package/dist/lib/progress.js +0 -6
- package/dist/lib/select.d.ts +0 -1
- package/dist/lib/select.js +0 -3
- package/dist/primitives/progress.js +0 -7
- package/gates/canonical.test.mjs +0 -71
- package/gates/extensions.test.mjs +0 -373
- package/gates/index.d.mts +0 -51
- package/gates/index.mjs +0 -514
- package/gates/tokens.test.mjs +0 -276
- package/shadcn/README.md +0 -13
- package/shadcn/badge.tsx +0 -49
- package/shadcn/button.tsx +0 -67
- package/shadcn/combobox.tsx +0 -299
- package/shadcn/dialog.tsx +0 -166
- package/shadcn/dropdown-menu.tsx +0 -267
- package/shadcn/field.tsx +0 -236
- package/shadcn/input-group.tsx +0 -154
- package/shadcn/progress.tsx +0 -29
- package/shadcn/sheet.tsx +0 -145
- package/shadcn/sonner.tsx +0 -49
- package/src/lib/button.ts +0 -4
- package/src/lib/cn.test.ts +0 -33
- package/src/lib/cn.ts +0 -23
- package/src/lib/menu.ts +0 -16
- package/src/lib/progress.ts +0 -6
- package/src/lib/select.ts +0 -4
- package/src/primitives/alert.tsx +0 -76
- package/src/primitives/card.tsx +0 -103
- package/src/primitives/input.tsx +0 -19
- package/src/primitives/label.tsx +0 -22
- package/src/primitives/select.tsx +0 -190
- package/src/primitives/separator.tsx +0 -28
- package/src/primitives/skeleton.tsx +0 -13
- package/src/primitives/sonner.tsx +0 -65
- package/src/primitives/table.tsx +0 -114
- package/src/primitives/tabs.tsx +0 -88
- package/src/primitives/textarea.tsx +0 -18
- package/src/primitives/tooltip.tsx +0 -57
- /package/dist/{primitives → components}/alert.d.ts +0 -0
- /package/dist/{primitives → components}/badge.d.ts +0 -0
- /package/dist/{primitives → components}/combobox.d.ts +0 -0
- /package/dist/{primitives → components}/dialog.d.ts +0 -0
- /package/dist/{primitives → components}/dropdown-menu.d.ts +0 -0
- /package/dist/{primitives → components}/field.d.ts +0 -0
- /package/dist/{primitives → components}/input-group.d.ts +0 -0
- /package/dist/{primitives → components}/input.d.ts +0 -0
- /package/dist/{primitives → components}/label.d.ts +0 -0
- /package/dist/{primitives → components}/progress.d.ts +0 -0
- /package/dist/{primitives → components}/separator.d.ts +0 -0
- /package/dist/{primitives → components}/sheet.d.ts +0 -0
- /package/dist/{primitives → components}/skeleton.d.ts +0 -0
- /package/dist/{primitives → components}/sonner.d.ts +0 -0
- /package/dist/{primitives → components}/tabs.d.ts +0 -0
- /package/dist/{primitives → components}/textarea.d.ts +0 -0
- /package/dist/{primitives → components}/tooltip.d.ts +0 -0
- /package/dist/{statusTone.d.ts → theme/statusTone.d.ts} +0 -0
- /package/dist/{statusTone.js → theme/statusTone.js} +0 -0
- /package/src/{statusTone.ts → theme/statusTone.ts} +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
/**
|
|
3
|
+
* Everything `styles.css` imports really resolves — packages and siblings both.
|
|
4
|
+
*
|
|
5
|
+
* The stylesheet ships as **source**: `exports["./styles.css"]` hands a consumer
|
|
6
|
+
* the file itself, and the consumer's Tailwind build is what resolves the
|
|
7
|
+
* `@import`s in it. So a package named there is a genuine runtime dependency of
|
|
8
|
+
* this one, and a sibling named there is a file that has to be published. Get
|
|
9
|
+
* either wrong and every consumer breaks while `pnpm lint`, `pnpm build` and
|
|
10
|
+
* `pnpm test` all stay green — nothing in this repository compiles CSS, so
|
|
11
|
+
* nothing here would otherwise notice. A rule nothing checks is a preference;
|
|
12
|
+
* this is the check.
|
|
13
|
+
*
|
|
14
|
+
* Both halves matter, and they fail differently. A missing **package** is a
|
|
15
|
+
* dependency that was dropped from `package.json` because a grep for
|
|
16
|
+
* `from "..."` never sees a CSS `@import`. A missing **sibling** is a file that
|
|
17
|
+
* exists on the author's disk but was never committed, or was left out of
|
|
18
|
+
* `files` — so it resolves here and nowhere else.
|
|
19
|
+
*
|
|
20
|
+
* Package resolution goes through the package's own `exports` map with the
|
|
21
|
+
* `style` condition first, because that is the condition a CSS bundler asks for
|
|
22
|
+
* and some packages expose their stylesheet under no other — Node's own
|
|
23
|
+
* `import.meta.resolve` asks for `import`/`require` and would report a
|
|
24
|
+
* correctly installed package as missing.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { spawnSync } from "node:child_process";
|
|
28
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
import { fileURLToPath } from "node:url";
|
|
31
|
+
|
|
32
|
+
import { expect, test } from "vitest";
|
|
33
|
+
|
|
34
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
35
|
+
const MODULES = path.join(REPO, "node_modules");
|
|
36
|
+
const STYLESHEET_DIR = path.join(REPO, "src/theme");
|
|
37
|
+
const STYLESHEET = readFileSync(path.join(STYLESHEET_DIR, "styles.css"), "utf8");
|
|
38
|
+
|
|
39
|
+
/** The conditions a CSS bundler offers, most specific first. */
|
|
40
|
+
const CONDITIONS = ["style", "default", "import", "require"];
|
|
41
|
+
|
|
42
|
+
/** Every `@import` specifier in the stylesheet, in source order. */
|
|
43
|
+
function importsIn(css: string): string[] {
|
|
44
|
+
return [...css.matchAll(/^\s*@import\s+["']([^"']+)["']/gm)].map((match) => match[1]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A URL needs no filesystem, so it is not this gate's business. */
|
|
48
|
+
const isRemote = (spec: string) => /^https?:/.test(spec);
|
|
49
|
+
|
|
50
|
+
/** `"./tailwind.css"`, `"../a/b.css"` — resolved against the stylesheet itself. */
|
|
51
|
+
const isRelative = (spec: string) => /^\.{1,2}\//.test(spec);
|
|
52
|
+
|
|
53
|
+
/** `"@scope/pkg/a/b"` → `["@scope/pkg", "./a/b"]`; `"pkg"` → `["pkg", "."]`. */
|
|
54
|
+
function splitSpecifier(spec: string): [name: string, subpath: string] {
|
|
55
|
+
const segments = spec.split("/");
|
|
56
|
+
const name = spec.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
|
|
57
|
+
const rest = spec.slice(name.length);
|
|
58
|
+
return [name, rest === "" ? "." : `.${rest}`];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The first branch of a conditional export any of `CONDITIONS` selects. */
|
|
62
|
+
function pickCondition(node: unknown): string | null {
|
|
63
|
+
if (typeof node === "string") return node;
|
|
64
|
+
if (node === null || typeof node !== "object") return null;
|
|
65
|
+
const branches = node as Record<string, unknown>;
|
|
66
|
+
for (const condition of CONDITIONS) {
|
|
67
|
+
if (condition in branches) {
|
|
68
|
+
const picked = pickCondition(branches[condition]);
|
|
69
|
+
if (picked !== null) return picked;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Where a package's `exports` (or its legacy fields) send one subpath. */
|
|
76
|
+
function targetOf(manifest: Record<string, unknown>, subpath: string): string | null {
|
|
77
|
+
const map = manifest.exports;
|
|
78
|
+
if (map === undefined) {
|
|
79
|
+
// No exports map: "." is the package's own stylesheet field, and any other
|
|
80
|
+
// subpath is a plain path inside the package.
|
|
81
|
+
if (subpath !== ".") return subpath;
|
|
82
|
+
return (manifest.style as string) ?? (manifest.main as string) ?? null;
|
|
83
|
+
}
|
|
84
|
+
if (typeof map === "string") return subpath === "." ? map : null;
|
|
85
|
+
const entries = map as Record<string, unknown>;
|
|
86
|
+
if (subpath in entries) return pickCondition(entries[subpath]);
|
|
87
|
+
// An exports map that is conditions all the way down is shorthand for ".".
|
|
88
|
+
if (subpath === "." && !Object.keys(entries).some((key) => key.startsWith("."))) {
|
|
89
|
+
return pickCondition(entries);
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The file a specifier lands on, or why it lands nowhere. */
|
|
95
|
+
function resolveStyleImport(spec: string): { file: string } | { reason: string } {
|
|
96
|
+
const [name, subpath] = splitSpecifier(spec);
|
|
97
|
+
const root = path.join(MODULES, ...name.split("/"));
|
|
98
|
+
const manifestPath = path.join(root, "package.json");
|
|
99
|
+
if (!existsSync(manifestPath)) {
|
|
100
|
+
return { reason: `${name} is not installed — it is missing from package.json dependencies` };
|
|
101
|
+
}
|
|
102
|
+
const target = targetOf(JSON.parse(readFileSync(manifestPath, "utf8")), subpath);
|
|
103
|
+
if (target === null) {
|
|
104
|
+
return { reason: `${name} declares no export for ${subpath}` };
|
|
105
|
+
}
|
|
106
|
+
const file = path.join(root, target);
|
|
107
|
+
if (!existsSync(file)) {
|
|
108
|
+
return { reason: `${name} exports ${subpath} as ${target}, which does not exist` };
|
|
109
|
+
}
|
|
110
|
+
return { file };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
test("every package the stylesheet imports is installed and exports the file it names", () => {
|
|
114
|
+
const specs = importsIn(STYLESHEET).filter((spec) => !isRemote(spec) && !isRelative(spec));
|
|
115
|
+
expect(specs.length > 0, "the stylesheet named no packages, so this proves nothing").toBe(true);
|
|
116
|
+
|
|
117
|
+
const broken = specs
|
|
118
|
+
.map((spec) => ({ spec, result: resolveStyleImport(spec) }))
|
|
119
|
+
.filter((entry) => "reason" in entry.result)
|
|
120
|
+
.map((entry) => `@import "${entry.spec}": ${(entry.result as { reason: string }).reason}`);
|
|
121
|
+
|
|
122
|
+
expect(
|
|
123
|
+
broken,
|
|
124
|
+
"styles.css ships as source and the consumer's Tailwind build resolves these, so an " +
|
|
125
|
+
"unresolvable @import breaks every consumer and nothing in this repository compiles " +
|
|
126
|
+
`CSS to catch it:\n${broken.join("\n")}`,
|
|
127
|
+
).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("every sibling the stylesheet imports exists and is committed", () => {
|
|
131
|
+
const specs = importsIn(STYLESHEET).filter(isRelative);
|
|
132
|
+
expect(specs.length > 0, "the stylesheet named no siblings, so this proves nothing").toBe(true);
|
|
133
|
+
|
|
134
|
+
const broken = specs.flatMap((spec) => {
|
|
135
|
+
const file = path.resolve(STYLESHEET_DIR, spec);
|
|
136
|
+
if (!existsSync(file)) {
|
|
137
|
+
return [`@import "${spec}": ${path.relative(REPO, file)} does not exist`];
|
|
138
|
+
}
|
|
139
|
+
// On disk is not enough. `files: ["src"]` publishes what the working tree
|
|
140
|
+
// holds, so an uncommitted sibling packs fine here and is absent from the
|
|
141
|
+
// clone CI builds and publishes from — the failure lands on consumers only.
|
|
142
|
+
const tracked = spawnSync("git", ["ls-files", "--error-unmatch", file], {
|
|
143
|
+
cwd: REPO,
|
|
144
|
+
encoding: "utf8",
|
|
145
|
+
});
|
|
146
|
+
if (tracked.status !== 0) {
|
|
147
|
+
return [
|
|
148
|
+
`@import "${spec}": ${path.relative(REPO, file)} exists but is not tracked by git — ` +
|
|
149
|
+
"commit it, or the published package will not carry it",
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
return [];
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
broken,
|
|
157
|
+
"styles.css ships as source, so a sibling it imports has to reach the consumer with it:\n" +
|
|
158
|
+
broken.join("\n"),
|
|
159
|
+
).toEqual([]);
|
|
160
|
+
});
|
|
@@ -13,7 +13,11 @@ describe("status tones", () => {
|
|
|
13
13
|
expect(STATUS_INK.warning).toBe("text-amber-700 dark:text-amber-400");
|
|
14
14
|
expect(STATUS_INK.success).toBe("text-emerald-700 dark:text-emerald-400");
|
|
15
15
|
expect(STATUS_INK.info).toBe("text-sky-700 dark:text-sky-400");
|
|
16
|
-
for (const v of [
|
|
16
|
+
for (const v of [
|
|
17
|
+
...Object.values(TONE_FILL),
|
|
18
|
+
...Object.values(TONE_BORDER),
|
|
19
|
+
...Object.values(STATUS_INK),
|
|
20
|
+
]) {
|
|
17
21
|
expect(v).not.toMatch(/\b(?:bg|border|text)-(?:success|warning)\b/);
|
|
18
22
|
}
|
|
19
23
|
});
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* @robomous/ui-core — the design system's stylesheet.
|
|
3
3
|
*
|
|
4
|
-
* This is the
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
4
|
+
* **This file is the design system's one home for a colour.** Tailwind v4 is
|
|
5
|
+
* CSS-first, so this is not a mirror of a config — it *is* the config, and
|
|
6
|
+
* every utility in this package and in every consuming app resolves through
|
|
7
|
+
* the names below. `brand` is Robomous's own identity colour, justified in
|
|
8
|
+
* `DESIGN.md`, and it is declared the way every other name here is: a value in
|
|
9
|
+
* `:root`, a dark counterpart in `.dark`, exposure through `@theme inline`.
|
|
10
|
+
* Consumers add their extensions in their own stylesheet after importing this
|
|
11
|
+
* one. `tokens.ts` is the TypeScript mirror of this file, for callers that
|
|
12
|
+
* cannot read CSS, and `tokens.test.ts` asserts the two agree declaration for
|
|
13
|
+
* declaration.
|
|
14
|
+
*
|
|
15
|
+
* `./tailwind.css` carries the variant and utility layer the components paint
|
|
16
|
+
* through: the `data-*` variants that key off the `data-state` attributes the
|
|
17
|
+
* behaviour libraries emit, plus the scroll-mask and shimmer utilities. It is
|
|
18
|
+
* imported ahead of the token blocks because a variant has to exist before a
|
|
19
|
+
* utility can qualify on it.
|
|
15
20
|
*
|
|
16
21
|
* A consuming app imports exactly this:
|
|
17
22
|
*
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
|
|
25
30
|
@import "tailwindcss";
|
|
26
31
|
@import "tw-animate-css";
|
|
27
|
-
@import "
|
|
32
|
+
@import "./tailwind.css";
|
|
28
33
|
@import "@fontsource-variable/geist";
|
|
29
34
|
|
|
30
35
|
/*
|
|
@@ -38,15 +43,15 @@
|
|
|
38
43
|
|
|
39
44
|
:root {
|
|
40
45
|
--background: oklch(1 0 0);
|
|
41
|
-
--foreground: oklch(0.
|
|
46
|
+
--foreground: oklch(0.2 0 0);
|
|
42
47
|
--card: oklch(1 0 0);
|
|
43
48
|
--card-foreground: oklch(0.145 0 0);
|
|
44
49
|
--popover: oklch(1 0 0);
|
|
45
50
|
--popover-foreground: oklch(0.145 0 0);
|
|
46
|
-
--primary: oklch(0.
|
|
51
|
+
--primary: oklch(0.2 0 0);
|
|
47
52
|
--primary-foreground: oklch(0.985 0 0);
|
|
48
53
|
--secondary: oklch(0.97 0 0);
|
|
49
|
-
--secondary-foreground: oklch(0.
|
|
54
|
+
--secondary-foreground: oklch(0.269 0 0);
|
|
50
55
|
--muted: oklch(0.97 0 0);
|
|
51
56
|
--muted-foreground: oklch(0.556 0 0);
|
|
52
57
|
--accent: oklch(0.97 0 0);
|
|
@@ -71,11 +76,15 @@
|
|
|
71
76
|
--sidebar-ring: oklch(0.708 0 0);
|
|
72
77
|
|
|
73
78
|
/*
|
|
74
|
-
* Robomous
|
|
75
|
-
* named in `DESIGN.md` — never a functional-UI colour. A third site
|
|
79
|
+
* Robomous orange (#F5580B). Identity only — the wordmark and its styleguide
|
|
80
|
+
* swatch, named in `DESIGN.md` — never a functional-UI colour. A third site
|
|
76
81
|
* is a design decision, not a styling one.
|
|
82
|
+
*
|
|
83
|
+
* At 3.34:1 on `background` it clears 3:1 for large text and non-text marks
|
|
84
|
+
* and misses 4.5:1 for body copy, which is one more reason the wordmark is
|
|
85
|
+
* where it belongs.
|
|
77
86
|
*/
|
|
78
|
-
--brand: oklch(0.
|
|
87
|
+
--brand: oklch(0.663 0.205 39.9);
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
.dark {
|
|
@@ -111,11 +120,11 @@
|
|
|
111
120
|
--sidebar-border: oklch(1 0 0 / 10%);
|
|
112
121
|
--sidebar-ring: oklch(0.556 0 0);
|
|
113
122
|
|
|
114
|
-
--brand: oklch(0.
|
|
123
|
+
--brand: oklch(0.663 0.205 39.9);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
@theme inline {
|
|
118
|
-
--font-sans:
|
|
127
|
+
--font-sans: "Geist Variable", sans-serif;
|
|
119
128
|
--font-heading: var(--font-sans);
|
|
120
129
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
121
130
|
--color-sidebar-border: var(--sidebar-border);
|
|
@@ -156,15 +165,16 @@
|
|
|
156
165
|
--radius-3xl: calc(var(--radius) * 2.2);
|
|
157
166
|
--radius-4xl: calc(var(--radius) * 2.6);
|
|
158
167
|
|
|
159
|
-
/* Robomous's one identity token, exposed the same way as every
|
|
168
|
+
/* Robomous's one identity token, exposed the same way as every other colour. */
|
|
160
169
|
--color-brand: var(--brand);
|
|
161
170
|
}
|
|
162
171
|
|
|
163
172
|
@layer base {
|
|
164
173
|
/* The hairline and the outline *colour* — the two things every element
|
|
165
|
-
inherits. The focus treatment itself belongs to the
|
|
166
|
-
carries
|
|
167
|
-
focus), so the base layer names the colour and stays out of
|
|
174
|
+
inherits. The focus treatment itself belongs to the components: each
|
|
175
|
+
focusable one carries its own `ring-3` ring in `ring/50` (DESIGN.md,
|
|
176
|
+
Borders and focus), so the base layer names the colour and stays out of
|
|
177
|
+
the geometry.
|
|
168
178
|
Nothing here may declare focus geometry again: one blanket declaration in
|
|
169
179
|
this layer is how thirteen components' rings got overridden at once. */
|
|
170
180
|
* {
|
|
@@ -186,19 +196,23 @@
|
|
|
186
196
|
body {
|
|
187
197
|
@apply bg-background text-foreground;
|
|
188
198
|
}
|
|
189
|
-
/*
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
button:not(:disabled),
|
|
199
|
+
/* Scoped to what is actually pressable and away from what is not: a disabled
|
|
200
|
+
control keeps the arrow, because a hand over something that will not
|
|
201
|
+
respond is the cursor telling a lie. */
|
|
202
|
+
button:not(:disabled),
|
|
203
|
+
[role="button"]:not(:disabled) {
|
|
193
204
|
cursor: pointer;
|
|
194
205
|
}
|
|
195
206
|
html {
|
|
196
207
|
@apply font-sans;
|
|
197
208
|
}
|
|
198
|
-
/* One family now:
|
|
199
|
-
|
|
209
|
+
/* One family now: `--font-heading` resolves to `--font-sans`, so this rule
|
|
210
|
+
keeps the semantic hook without changing the face. A later decision that
|
|
200
211
|
splits them again lands here without a diff at every heading. */
|
|
201
|
-
h1,
|
|
212
|
+
h1,
|
|
213
|
+
h2,
|
|
214
|
+
h3,
|
|
215
|
+
h4 {
|
|
202
216
|
@apply font-heading;
|
|
203
217
|
}
|
|
204
218
|
}
|