@filipgutica/ui 1.2.0 → 1.4.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.
@@ -0,0 +1,6 @@
1
+ export { default as UiSiteHeader } from './UiSiteHeader.vue';
2
+ export type { UiSiteHeaderProps, UiSiteLink } from './UiSiteHeader.vue';
3
+ export { SITE_PROJECTS } from './projects.js';
4
+ export type { SiteProject, SiteProjectId } from './projects.js';
5
+ export { applySiteAppearance, SITE_APPEARANCE_KEY, siteThemeBootScript, useSiteAppearance, } from './appearance.js';
6
+ export type { SiteAppearance, SiteAppearanceChoice } from './appearance.js';
@@ -0,0 +1,36 @@
1
+ declare const projects: readonly [{
2
+ readonly id: "annoterm";
3
+ readonly label: "annoterm";
4
+ readonly href: "https://filipgutica.github.io/annoterm/";
5
+ readonly repository: "https://github.com/filipgutica/annoterm";
6
+ }, {
7
+ readonly id: "wtree";
8
+ readonly label: "wtree";
9
+ readonly href: "https://filipgutica.github.io/wtree/";
10
+ readonly repository: "https://github.com/filipgutica/wtree";
11
+ }, {
12
+ readonly id: "devps";
13
+ readonly label: "devps";
14
+ readonly href: "https://filipgutica.github.io/devps/";
15
+ readonly repository: "https://github.com/filipgutica/devps";
16
+ }, {
17
+ readonly id: "workbench";
18
+ readonly label: "Workbench";
19
+ readonly href: "https://filipgutica.github.io/t3code/";
20
+ readonly repository: "https://github.com/filipgutica/t3code";
21
+ }, {
22
+ readonly id: "ui";
23
+ readonly label: "UI";
24
+ readonly href: "https://filipgutica.github.io/ui/";
25
+ readonly repository: "https://github.com/filipgutica/ui";
26
+ }];
27
+ export type SiteProjectId = (typeof projects)[number]['id'];
28
+ export interface SiteProject {
29
+ readonly id: SiteProjectId;
30
+ readonly label: string;
31
+ readonly href: string;
32
+ readonly repository: string;
33
+ }
34
+ /** Every site in the family, in the order the project menu lists them. */
35
+ export declare const SITE_PROJECTS: readonly SiteProject[];
36
+ export {};
package/dist/site.js ADDED
@@ -0,0 +1,177 @@
1
+ import { Fragment as e, computed as t, createCommentVNode as n, createElementBlock as r, createElementVNode as i, defineComponent as a, onBeforeUnmount as o, onMounted as s, openBlock as c, ref as l, renderList as u, renderSlot as d, toDisplayString as f, unref as p, useSlots as m, watch as h } from "vue";
2
+ import { tryOnMounted as g, usePreferredDark as _, useStorage as v } from "@vueuse/core";
3
+ //#region src/site/appearance.ts
4
+ var y = "tool-site-theme", b = (e) => e === "system" || e === "light" || e === "dark", x = `(function () {
5
+ var root = document.documentElement, choice;
6
+ try { choice = localStorage.getItem('${y}'); } catch (error) {}
7
+ var dark = choice === 'dark';
8
+ if (choice !== 'light' && choice !== 'dark') {
9
+ try { dark = matchMedia('(prefers-color-scheme: dark)').matches; } catch (error) {}
10
+ }
11
+ root.classList.toggle('dark', dark);
12
+ if (choice === 'light' || choice === 'dark') root.dataset.theme = choice;
13
+ })();`, S = ({ root: e, choice: t, appearance: n }) => {
14
+ e.classList.toggle("dark", n === "dark"), t === "system" ? delete e.dataset.theme : e.dataset.theme = t;
15
+ }, C = ({ applyToRoot: e = !0 } = {}) => {
16
+ let n = v(y, "system", void 0, {
17
+ initOnMounted: !0,
18
+ writeDefaults: !1,
19
+ onError: () => {},
20
+ serializer: {
21
+ read: (e) => b(e) ? e : "system",
22
+ write: (e) => e
23
+ }
24
+ }), r = _(), i = t(() => n.value), a = t(() => n.value === "system" ? r.value ? "dark" : "light" : n.value);
25
+ return e && g(() => {
26
+ h([i, a], () => S({
27
+ root: document.documentElement,
28
+ choice: i.value,
29
+ appearance: a.value
30
+ }), { immediate: !0 });
31
+ }), {
32
+ choice: i,
33
+ appearance: a,
34
+ chooseAppearance: (e) => {
35
+ b(e) && (n.value = e);
36
+ }
37
+ };
38
+ }, w = [
39
+ {
40
+ id: "annoterm",
41
+ label: "annoterm",
42
+ href: "https://filipgutica.github.io/annoterm/",
43
+ repository: "https://github.com/filipgutica/annoterm"
44
+ },
45
+ {
46
+ id: "wtree",
47
+ label: "wtree",
48
+ href: "https://filipgutica.github.io/wtree/",
49
+ repository: "https://github.com/filipgutica/wtree"
50
+ },
51
+ {
52
+ id: "devps",
53
+ label: "devps",
54
+ href: "https://filipgutica.github.io/devps/",
55
+ repository: "https://github.com/filipgutica/devps"
56
+ },
57
+ {
58
+ id: "workbench",
59
+ label: "Workbench",
60
+ href: "https://filipgutica.github.io/t3code/",
61
+ repository: "https://github.com/filipgutica/t3code"
62
+ },
63
+ {
64
+ id: "ui",
65
+ label: "UI",
66
+ href: "https://filipgutica.github.io/ui/",
67
+ repository: "https://github.com/filipgutica/ui"
68
+ }
69
+ ], T = { class: "fg-site-header" }, E = { class: "fg-site-header__inner" }, D = ["aria-label"], O = {
70
+ class: "fg-site-projects__menu fg-site-nav",
71
+ "aria-label": "Projects"
72
+ }, k = ["href", "aria-current"], A = { class: "fg-site-header__end" }, j = {
73
+ key: 0,
74
+ class: "fg-site-header__links",
75
+ "aria-label": "Site links"
76
+ }, M = ["href"], N = {
77
+ key: 0,
78
+ class: "fg-site-appearance",
79
+ role: "group",
80
+ "aria-label": "Color theme"
81
+ }, P = [
82
+ "aria-label",
83
+ "title",
84
+ "aria-pressed",
85
+ "onClick"
86
+ ], F = {
87
+ viewBox: "0 0 24 24",
88
+ width: "18",
89
+ height: "18",
90
+ fill: "none",
91
+ stroke: "currentColor",
92
+ "stroke-width": "1.5",
93
+ "aria-hidden": "true"
94
+ }, I = {
95
+ key: 2,
96
+ d: "M20.5 13.5A9 9 0 0 1 10.5 3a9 9 0 1 0 10 10.5Z"
97
+ }, L = /* @__PURE__ */ a({
98
+ __name: "UiSiteHeader",
99
+ props: {
100
+ project: {},
101
+ links: { default: () => [] }
102
+ },
103
+ setup(a) {
104
+ let h = [
105
+ {
106
+ value: "system",
107
+ label: "System"
108
+ },
109
+ {
110
+ value: "light",
111
+ label: "Light"
112
+ },
113
+ {
114
+ value: "dark",
115
+ label: "Dark"
116
+ }
117
+ ], g = t(() => w.find(({ id: e }) => e === a.project)?.label ?? a.project), _ = m().default ? void 0 : C(), v = l(!1), y = l(), b = l(), x = () => {
118
+ y.value && (y.value.open = !1);
119
+ }, S = (e) => {
120
+ e.key !== "Escape" || !y.value?.open || (x(), b.value?.focus());
121
+ }, L = (e) => {
122
+ e.relatedTarget instanceof Node && !y.value?.contains(e.relatedTarget) && x();
123
+ }, R = (e) => {
124
+ e.target instanceof Node && !y.value?.contains(e.target) && x();
125
+ };
126
+ return s(() => {
127
+ v.value = !0, document.addEventListener("pointerdown", R);
128
+ }), o(() => document.removeEventListener("pointerdown", R)), (t, o) => (c(), r("header", T, [i("div", E, [i("details", {
129
+ ref_key: "menu",
130
+ ref: y,
131
+ class: "fg-site-projects",
132
+ onKeydown: S,
133
+ onFocusout: L
134
+ }, [i("summary", {
135
+ ref_key: "trigger",
136
+ ref: b,
137
+ class: "fg-site-projects__trigger",
138
+ "aria-label": `Project: ${g.value}`
139
+ }, [i("span", null, f(g.value), 1), o[0] ||= i("svg", {
140
+ class: "fg-site-projects__chevron",
141
+ viewBox: "0 0 16 16",
142
+ width: "16",
143
+ height: "16",
144
+ fill: "none",
145
+ stroke: "currentColor",
146
+ "stroke-width": "1.5",
147
+ "aria-hidden": "true"
148
+ }, [i("path", { d: "m4 6 4 4 4-4" })], -1)], 8, D), i("nav", O, [(c(!0), r(e, null, u(p(w), (e) => (c(), r("a", {
149
+ key: e.id,
150
+ href: e.href,
151
+ "aria-current": e.id === a.project ? "page" : void 0
152
+ }, f(e.label), 9, k))), 128))])], 544), i("div", A, [a.links.length > 0 ? (c(), r("nav", j, [(c(!0), r(e, null, u(a.links, (e) => (c(), r("a", {
153
+ key: e.href,
154
+ href: e.href
155
+ }, f(e.label), 9, M))), 128))])) : n("", !0), d(t.$slots, "default", {}, () => [v.value && p(_) ? (c(), r("div", N, [(c(), r(e, null, u(h, (t) => i("button", {
156
+ key: t.value,
157
+ type: "button",
158
+ class: "fg-site-appearance__choice",
159
+ "aria-label": `${t.label} theme`,
160
+ title: `${t.label} theme`,
161
+ "aria-pressed": p(_).choice.value === t.value,
162
+ onClick: (e) => p(_).chooseAppearance(t.value)
163
+ }, [(c(), r("svg", F, [t.value === "system" ? (c(), r(e, { key: 0 }, [o[1] ||= i("rect", {
164
+ x: "3",
165
+ y: "4",
166
+ width: "18",
167
+ height: "13",
168
+ rx: "2"
169
+ }, null, -1), o[2] ||= i("path", { d: "M8 21h8m-4-4v4" }, null, -1)], 64)) : t.value === "light" ? (c(), r(e, { key: 1 }, [o[3] ||= i("circle", {
170
+ cx: "12",
171
+ cy: "12",
172
+ r: "4"
173
+ }, null, -1), o[4] ||= i("path", { d: "M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1.5 1.5m11 11L19 19M5 19l1.5-1.5m11-11L19 5" }, null, -1)], 64)) : (c(), r("path", I))]))], 8, P)), 64))])) : n("", !0)])])])]));
174
+ }
175
+ });
176
+ //#endregion
177
+ export { y as SITE_APPEARANCE_KEY, w as SITE_PROJECTS, L as UiSiteHeader, S as applySiteAppearance, x as siteThemeBootScript, C as useSiteAppearance };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@filipgutica/ui",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "A compact Vue design system with Tailwind CSS and VS Code theme support",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -11,6 +11,8 @@
11
11
  "src/theme.css",
12
12
  "src/components.css",
13
13
  "src/components/*.css",
14
+ "src/site.css",
15
+ "src/themes/*.css",
14
16
  "README.md",
15
17
  "LICENSE"
16
18
  ],
@@ -23,12 +25,18 @@
23
25
  "types": "./dist/theme/index.d.ts",
24
26
  "import": "./dist/theme.js"
25
27
  },
28
+ "./site": {
29
+ "types": "./dist/site/index.d.ts",
30
+ "import": "./dist/site.js"
31
+ },
26
32
  "./open-vsx": {
27
33
  "types": "./dist/open-vsx/index.d.ts",
28
34
  "import": "./dist/open-vsx.js"
29
35
  },
30
36
  "./theme.css": "./src/theme.css",
31
- "./components.css": "./src/components.css"
37
+ "./components.css": "./src/components.css",
38
+ "./site.css": "./src/site.css",
39
+ "./themes/graphite-indigo.css": "./src/themes/graphite-indigo.css"
32
40
  },
33
41
  "scripts": {
34
42
  "build": "vite build && vue-tsc -p tsconfig.build.json --emitDeclarationOnly",
@@ -44,8 +52,10 @@
44
52
  "release:dry-run": "semantic-release --dry-run --no-ci",
45
53
  "test": "vitest run",
46
54
  "test:e2e": "playwright test",
55
+ "themes:build": "node scripts/graphite-indigo.mjs",
56
+ "themes:check": "node scripts/graphite-indigo.mjs --check",
47
57
  "typecheck": "vue-tsc -p tsconfig.json --noEmit",
48
- "verify": "pnpm lint && pnpm typecheck && pnpm docs:typecheck && pnpm test && pnpm test:e2e && pnpm build && pnpm docs:build && pnpm pack --dry-run && publint"
58
+ "verify": "pnpm lint && pnpm themes:check && pnpm typecheck && pnpm docs:typecheck && pnpm test && pnpm test:e2e && pnpm build && pnpm docs:build && pnpm pack --dry-run && publint"
49
59
  },
50
60
  "engines": {
51
61
  "node": ">=22.12"
@@ -61,6 +61,51 @@
61
61
  .fg-code-block[data-wrap="true"] .fg-code-block__text { min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
62
62
  .fg-code-block__status:empty { display: none; }
63
63
  .fg-code-block__status:not(:empty) { display: block; padding: 0.5rem 0.75rem; font-size: 0.75rem; }
64
+ .fg-code-block[data-variant="compact"] {
65
+ position: relative;
66
+ display: grid;
67
+ grid-template-columns: minmax(0, 1fr) auto;
68
+ align-items: center;
69
+ }
70
+ .fg-code-block[data-variant="compact"] .fg-code-block__header {
71
+ grid-column: 2;
72
+ grid-row: 1;
73
+ min-height: 0;
74
+ padding: var(--space-1);
75
+ border: 0;
76
+ background: transparent;
77
+ }
78
+ .fg-code-block[data-variant="compact"] .fg-code-block__actions {
79
+ gap: var(--space-1);
80
+ margin-left: 0;
81
+ }
82
+ .fg-code-block[data-variant="compact"] .fg-code-block__copy {
83
+ padding: 0;
84
+ border-radius: var(--radius-control);
85
+ }
86
+ .fg-code-block[data-variant="compact"] .fg-code-block__pre {
87
+ grid-column: 1;
88
+ grid-row: 1;
89
+ min-width: 0;
90
+ padding: var(--space-2) var(--space-3);
91
+ }
92
+ .fg-code-block[data-variant="compact"] .fg-code-block__status:not(:empty) {
93
+ grid-column: 1 / -1;
94
+ padding: var(--space-2) var(--space-3);
95
+ font-size: var(--text-sm);
96
+ overflow-wrap: anywhere;
97
+ }
98
+ .fg-code-block[data-variant="compact"] .fg-code-block__status[data-state="success"] {
99
+ position: absolute;
100
+ top: 0;
101
+ left: 0;
102
+ width: 1px;
103
+ height: 1px;
104
+ padding: 0;
105
+ overflow: hidden;
106
+ clip-path: inset(50%);
107
+ white-space: nowrap;
108
+ }
64
109
  .fg-button,
65
110
  .fg-input,
66
111
  .fg-select,
@@ -180,14 +225,14 @@
180
225
  }
181
226
 
182
227
  .fg-button[data-variant="danger"] {
183
- color: var(--color-error);
228
+ color: var(--color-status-text, var(--color-error));
184
229
  background: var(--color-error-surface);
185
230
  box-shadow: none;
186
231
  }
187
232
 
188
233
  .fg-button[data-variant="danger"]:hover:not(:disabled) {
189
234
  border-color: var(--color-error);
190
- color: var(--color-error);
235
+ color: var(--color-status-text, var(--color-error));
191
236
  background: var(--color-error-surface);
192
237
  box-shadow: none;
193
238
  }
@@ -477,11 +522,11 @@
477
522
  line-height: 1.25;
478
523
  }
479
524
 
480
- .fg-badge[data-tone="info"] { color: var(--color-info); background: var(--color-info-surface); }
481
- .fg-badge[data-tone="success"] { color: var(--color-success); background: var(--color-success-surface); }
482
- .fg-badge[data-tone="warning"] { color: var(--color-warning); background: var(--color-warning-surface); }
525
+ .fg-badge[data-tone="info"] { color: var(--color-status-text, var(--color-info)); background: var(--color-info-surface); }
526
+ .fg-badge[data-tone="success"] { color: var(--color-status-text, var(--color-success)); background: var(--color-success-surface); }
527
+ .fg-badge[data-tone="warning"] { color: var(--color-status-text, var(--color-warning)); background: var(--color-warning-surface); }
483
528
  .fg-badge[data-tone="danger"],
484
- .fg-badge[data-tone="error"] { color: var(--color-error); background: var(--color-error-surface); }
529
+ .fg-badge[data-tone="error"] { color: var(--color-status-text, var(--color-error)); background: var(--color-error-surface); }
485
530
 
486
531
  .fg-alert {
487
532
  display: flex;
@@ -500,7 +545,7 @@
500
545
  .fg-alert[data-tone="danger"],
501
546
  .fg-alert[data-tone="error"] {
502
547
  border-color: var(--color-error);
503
- color: var(--color-error);
548
+ color: var(--color-status-text, var(--color-error));
504
549
  background: var(--color-error-surface);
505
550
  }
506
551
 
package/src/site.css ADDED
@@ -0,0 +1,169 @@
1
+ /* Shared chrome and page helpers for the project sites. Use with
2
+ * @filipgutica/ui/themes/graphite-indigo.css, which supplies the type, space, and
3
+ * page-measure roles this file reads. Nothing here targets site-specific markup. */
4
+ @layer components {
5
+ /* A centered frame. The header uses the same edges as the page below it.
6
+ * Set --site-frame on the header or a frame to change its width. */
7
+ .fg-site-frame,
8
+ .fg-site-header__inner {
9
+ width: min(100%, var(--site-frame, var(--page-max)));
10
+ margin-inline: auto;
11
+ padding-inline: var(--site-gutter, var(--space-4));
12
+ }
13
+ .fg-site-frame--wide { --site-frame: var(--page-wide); }
14
+
15
+ .fg-site-header {
16
+ position: relative;
17
+ z-index: 30;
18
+ border-bottom: 1px solid var(--color-border-subtle);
19
+ background: var(--color-bg);
20
+ color: var(--color-text);
21
+ font-family: var(--font-ui);
22
+ font-size: var(--text-md);
23
+ line-height: var(--leading);
24
+ }
25
+ .fg-site-header__inner {
26
+ display: flex;
27
+ min-height: var(--space-16);
28
+ flex-wrap: wrap;
29
+ align-items: center;
30
+ gap: var(--space-2) var(--space-4);
31
+ padding-block: var(--space-2);
32
+ }
33
+ .fg-site-header__end {
34
+ display: flex;
35
+ flex-wrap: wrap;
36
+ align-items: center;
37
+ gap: var(--space-2) var(--space-8);
38
+ margin-inline-start: auto;
39
+ }
40
+ .fg-site-header__links { display: flex; align-items: center; gap: var(--space-6); }
41
+ .fg-site-header__links a {
42
+ display: inline-flex;
43
+ min-height: var(--control-lg);
44
+ align-items: center;
45
+ color: var(--color-muted);
46
+ font-size: var(--text-sm);
47
+ text-decoration: none;
48
+ }
49
+ .fg-site-header__links a:hover { color: var(--color-text); }
50
+
51
+ .fg-site-projects { position: relative; }
52
+ .fg-site-projects__trigger {
53
+ display: inline-flex;
54
+ min-height: var(--control-lg);
55
+ align-items: center;
56
+ gap: var(--space-1);
57
+ padding-inline: var(--space-3);
58
+ border-radius: var(--radius-control);
59
+ color: var(--color-text);
60
+ font-weight: var(--weight-semibold);
61
+ list-style: none;
62
+ cursor: pointer;
63
+ user-select: none;
64
+ }
65
+ .fg-site-projects__trigger::-webkit-details-marker { display: none; }
66
+ .fg-site-projects__trigger:hover,
67
+ .fg-site-projects[open] > .fg-site-projects__trigger { background: var(--color-row-hover); }
68
+ .fg-site-projects__chevron { transition: transform var(--duration-fast) ease; }
69
+ .fg-site-projects[open] .fg-site-projects__chevron { transform: rotate(180deg); }
70
+ .fg-site-projects__menu {
71
+ position: absolute;
72
+ z-index: 1;
73
+ top: 100%;
74
+ left: 0;
75
+ min-width: 14rem;
76
+ margin-top: var(--space-1);
77
+ padding: var(--space-1);
78
+ border-radius: var(--radius-panel);
79
+ background: var(--color-surface);
80
+ box-shadow: var(--shadow-dialog);
81
+ }
82
+
83
+ /* Route rows. Hover is neutral, and the current page keeps its selected fill on
84
+ * hover. Only aria-current="page" is styled: an in-page section marker
85
+ * (aria-current="location") must look different from the current page. */
86
+ .fg-site-nav a {
87
+ display: flex;
88
+ min-height: var(--row);
89
+ align-items: center;
90
+ padding-inline: var(--space-3);
91
+ border-radius: var(--radius-control);
92
+ color: var(--color-muted);
93
+ font-size: var(--text-md);
94
+ text-decoration: none;
95
+ transition: background-color var(--duration-fast) ease, color var(--duration-fast) ease;
96
+ }
97
+ .fg-site-nav a:hover { background: var(--color-row-hover); color: var(--color-text); }
98
+ .fg-site-nav a[aria-current="page"] {
99
+ background: var(--color-row-selected);
100
+ box-shadow: inset 2px 0 0 var(--color-accent);
101
+ color: var(--color-text);
102
+ font-weight: var(--weight-medium);
103
+ }
104
+
105
+ .fg-site-appearance {
106
+ display: inline-flex;
107
+ flex: none;
108
+ border: 1px solid var(--color-border-subtle);
109
+ border-radius: var(--radius-control);
110
+ }
111
+ .fg-site-appearance__choice {
112
+ display: inline-flex;
113
+ width: var(--control-lg);
114
+ min-height: var(--control-lg);
115
+ align-items: center;
116
+ justify-content: center;
117
+ padding: 0;
118
+ border: 0;
119
+ color: var(--color-muted);
120
+ background: transparent;
121
+ font: inherit;
122
+ cursor: pointer;
123
+ }
124
+ .fg-site-appearance__choice + .fg-site-appearance__choice { border-left: 1px solid var(--color-border-subtle); }
125
+ .fg-site-appearance__choice:first-child { border-radius: var(--radius-control) 0 0 var(--radius-control); }
126
+ .fg-site-appearance__choice:last-child { border-radius: 0 var(--radius-control) var(--radius-control) 0; }
127
+ .fg-site-appearance__choice:hover { color: var(--color-text); background: var(--color-row-hover); }
128
+ .fg-site-appearance__choice[aria-pressed="true"] { color: var(--color-text); background: var(--color-row-selected); }
129
+
130
+ .fg-site-header a:focus-visible,
131
+ .fg-site-projects__trigger:focus-visible,
132
+ .fg-site-appearance__choice:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
133
+ .fg-site-appearance__choice:focus-visible { outline-offset: -2px; }
134
+ .fg-site-nav a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: -2px; }
135
+
136
+ /* Type roles for article content. Prose reads at --text-reading. Components set
137
+ * their own size, and controls and dense content such as tables belong at --text-md.
138
+ * Zero-specificity heading selectors keep component and page styles in charge. */
139
+ .fg-site-prose { font-size: var(--text-reading); line-height: var(--leading); }
140
+ .fg-site-prose :where(h1, h2, h3, h4) {
141
+ margin: 0;
142
+ font-weight: var(--weight-semibold);
143
+ line-height: var(--leading-tight);
144
+ letter-spacing: var(--tracking-tight);
145
+ text-wrap: balance;
146
+ }
147
+ .fg-site-prose :where(h1) { font-size: var(--text-3xl); }
148
+ .fg-site-prose :where(h2) { font-size: var(--text-xl); }
149
+ .fg-site-prose :where(h3, h4) { font-size: var(--text-lg); }
150
+ .fg-site-prose :where(p, ul, ol) { max-width: var(--measure); margin: 0; }
151
+ .fg-site-prose :where(p, ul, ol) + :where(p, ul, ol) { margin-top: var(--space-4); }
152
+ .fg-site-prose :where(p a, li a, dd a) { text-decoration: underline; text-underline-offset: 0.18em; }
153
+ .fg-site-meta { color: var(--color-muted); font-size: var(--text-sm); }
154
+
155
+ @media (min-width: 681px) {
156
+ .fg-site-frame,
157
+ .fg-site-header__inner { padding-inline: var(--site-gutter, var(--space-6)); }
158
+ }
159
+ @media (max-width: 680px) {
160
+ .fg-site-prose :where(h1) { font-size: var(--text-2xl); }
161
+ .fg-site-header__end { gap: var(--space-2) var(--space-4); }
162
+ .fg-site-header__links { gap: var(--space-4); }
163
+ .fg-site-projects__menu a { min-height: var(--control-lg); }
164
+ }
165
+ @media (prefers-reduced-motion: reduce) {
166
+ .fg-site-projects__chevron,
167
+ .fg-site-nav a { transition: none; }
168
+ }
169
+ }
@@ -0,0 +1,111 @@
1
+ /* Generated from themes/graphite-indigo.json by scripts/graphite-indigo.mjs. Do not edit.
2
+ * Opt-in Graphite Indigo roles for @filipgutica/ui. Import after theme.css.
3
+ * Dark mode uses the same .dark class as theme.css. Keep exactly one :root.dark block:
4
+ * sites copy it into a <noscript> prefers-color-scheme rule. */
5
+
6
+ :root {
7
+ color-scheme: light;
8
+ --color-bg: #f4f5f8;
9
+ --color-surface: #e7e8ec;
10
+ --color-surface-raised: #ffffff;
11
+ --color-surface-muted: #e4e6eb;
12
+ --color-text: #222326;
13
+ --color-muted: #5d626c;
14
+ --color-link: #075fc7;
15
+ --color-accent: #6359a7;
16
+ --color-focus: #075fc7;
17
+ --color-border: #777d88;
18
+ --color-border-subtle: #d8dbe2;
19
+ --color-button-bg: var(--color-accent);
20
+ --color-button-text: #ffffff;
21
+ --color-button-hover: #554b94;
22
+ --color-input-bg: var(--color-surface-raised);
23
+ --color-input-text: var(--color-text);
24
+ --color-input-border: var(--color-border);
25
+ --color-input-placeholder: var(--color-muted);
26
+ --color-row-hover: #dfe1e7;
27
+ --color-row-selected: #ffffff;
28
+ --color-row-selected-text: var(--color-text);
29
+ --color-code: var(--color-surface);
30
+ --color-code-text: var(--color-text);
31
+ --color-syntax-comment: #626772;
32
+ --color-syntax-keyword: #715b42;
33
+ --color-syntax-string: #346347;
34
+ --color-syntax-number: #86532e;
35
+ --color-syntax-function: var(--color-syntax-keyword);
36
+ --color-syntax-type: var(--color-syntax-number);
37
+ --color-syntax-variable: var(--color-code-text);
38
+ --color-syntax-operator: var(--color-muted);
39
+ --color-syntax-punctuation: var(--color-muted);
40
+ --color-syntax-tag: var(--color-syntax-keyword);
41
+ --color-syntax-attribute: var(--color-syntax-number);
42
+ --color-error: #c7372d;
43
+ --color-warning: #86532e;
44
+ --color-success: #1f8a5b;
45
+ --color-info: #175cd3;
46
+ --color-progress: var(--color-accent);
47
+ --color-error-surface: color-mix(in srgb, var(--color-error) 10%, var(--color-bg));
48
+ --color-warning-surface: color-mix(in srgb, var(--color-warning) 12%, var(--color-bg));
49
+ --color-success-surface: color-mix(in srgb, var(--color-success) 11%, var(--color-bg));
50
+ --color-info-surface: color-mix(in srgb, var(--color-info) 10%, var(--color-bg));
51
+ --shadow-dialog: 0 8px 24px rgb(16 16 24 / 12%), 0 0 0 1px var(--color-border-subtle);
52
+ --color-status-text: var(--color-text);
53
+ --text-3xl: 32px;
54
+ --text-reading: 16px;
55
+ --text-display: 56px;
56
+ --text-display-sm: 38px;
57
+ --tracking-tight: -0.01em;
58
+ --space-20: 80px;
59
+ --space-24: 96px;
60
+ --row: 36px;
61
+ --page-wide: 1120px;
62
+ }
63
+
64
+ :root.dark {
65
+ color-scheme: dark;
66
+ --color-bg: #151619;
67
+ --color-surface: #191a1d;
68
+ --color-surface-raised: #212226;
69
+ --color-surface-muted: #303238;
70
+ --color-text: #f4f5f8;
71
+ --color-muted: #b0b5bf;
72
+ --color-link: #58a6ff;
73
+ --color-accent: #a99ce2;
74
+ --color-focus: #58a6ff;
75
+ --color-border: #818894;
76
+ --color-border-subtle: #3b3e45;
77
+ --color-button-bg: var(--color-accent);
78
+ --color-button-text: #151619;
79
+ --color-button-hover: #b7ace9;
80
+ --color-input-bg: var(--color-surface-raised);
81
+ --color-input-text: var(--color-text);
82
+ --color-input-border: var(--color-border);
83
+ --color-input-placeholder: var(--color-muted);
84
+ --color-row-hover: #25272d;
85
+ --color-row-selected: #38334f;
86
+ --color-row-selected-text: var(--color-text);
87
+ --color-code: var(--color-surface);
88
+ --color-code-text: var(--color-text);
89
+ --color-syntax-comment: #a0a6b1;
90
+ --color-syntax-keyword: #d8c2a5;
91
+ --color-syntax-string: #a8bd9c;
92
+ --color-syntax-number: #d5b98c;
93
+ --color-syntax-function: var(--color-syntax-keyword);
94
+ --color-syntax-type: var(--color-syntax-number);
95
+ --color-syntax-variable: var(--color-code-text);
96
+ --color-syntax-operator: var(--color-muted);
97
+ --color-syntax-punctuation: var(--color-muted);
98
+ --color-syntax-tag: var(--color-syntax-keyword);
99
+ --color-syntax-attribute: var(--color-syntax-number);
100
+ --color-error: #f0776c;
101
+ --color-warning: #e2a54b;
102
+ --color-success: #4cc38a;
103
+ --color-info: #84adff;
104
+ --color-progress: var(--color-accent);
105
+ --color-error-surface: color-mix(in srgb, var(--color-error) 10%, var(--color-bg));
106
+ --color-warning-surface: color-mix(in srgb, var(--color-warning) 12%, var(--color-bg));
107
+ --color-success-surface: color-mix(in srgb, var(--color-success) 11%, var(--color-bg));
108
+ --color-info-surface: color-mix(in srgb, var(--color-info) 10%, var(--color-bg));
109
+ --shadow-dialog: 0 8px 24px rgb(0 0 0 / 45%), 0 0 0 1px var(--color-border);
110
+ --color-status-text: var(--color-text);
111
+ }