@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.
- package/README.md +47 -0
- package/dist/components/UiCodeBlock.vue.d.ts +1 -0
- package/dist/index.js +125 -108
- package/dist/site/UiSiteHeader.vue.d.ts +24 -0
- package/dist/site/appearance.d.ts +35 -0
- package/dist/site/index.d.ts +6 -0
- package/dist/site/projects.d.ts +36 -0
- package/dist/site.js +177 -0
- package/package.json +13 -3
- package/src/components.css +52 -7
- package/src/site.css +169 -0
- package/src/themes/graphite-indigo.css +111 -0
|
@@ -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.
|
|
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"
|
package/src/components.css
CHANGED
|
@@ -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
|
+
}
|