@motion-proto/live-tokens 0.5.0 → 0.6.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 -4
- package/package.json +10 -5
- package/src/component-editor/DialogEditor.svelte +2 -2
- package/src/component-editor/NotificationEditor.svelte +2 -2
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +2 -1
- package/src/pages/ComponentEditorPage.svelte +8 -1
- package/src/pages/Editor.svelte +8 -2
- package/src/styles/site.css +138 -0
- package/src/styles/tokens.css +24 -0
- package/src/styles/ui-form-controls.css +186 -0
- package/src/ui/FontStackEditor.svelte +1 -1
- package/src/ui/ProjectFontsSection.svelte +4 -4
- package/src/styles/form-controls.css +0 -188
package/README.md
CHANGED
|
@@ -127,13 +127,56 @@ The components carry their own design-token aliases (declared inside each `.svel
|
|
|
127
127
|
|
|
128
128
|
### Styles
|
|
129
129
|
|
|
130
|
+
Editor chrome (`ui-editor.css`, `ui-form-controls.css`) and the icon font are
|
|
131
|
+
**auto-loaded by the editor pages themselves**; you don't import them. The
|
|
132
|
+
only stylesheet a consumer needs is a `tokens.css` declaring the design-token
|
|
133
|
+
CSS variables on `:root`.
|
|
134
|
+
|
|
135
|
+
You can use the package's default as a starting point:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import '@motion-proto/live-tokens/starter/tokens.css';
|
|
139
|
+
import '@motion-proto/live-tokens/starter/site.css'; // optional: themed h1/p/a styles
|
|
140
|
+
import '@motion-proto/live-tokens/starter/fonts.css'; // optional: Fraunces + Manrope @font-face
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
…or copy `node_modules/@motion-proto/live-tokens/src/styles/tokens.css` into
|
|
144
|
+
your project and edit. The editor will seed `themes/default.json` on first
|
|
145
|
+
run and you can promote your edits back into the file.
|
|
146
|
+
|
|
147
|
+
## Consuming live-tokens from scratch
|
|
148
|
+
|
|
149
|
+
The minimum a consumer needs after `npm install @motion-proto/live-tokens`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// src/main.ts
|
|
153
|
+
import '@motion-proto/live-tokens/starter/tokens.css';
|
|
154
|
+
import { mount } from 'svelte';
|
|
155
|
+
import App from './App.svelte';
|
|
156
|
+
|
|
157
|
+
mount(App, { target: document.getElementById('app')! });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```svelte
|
|
161
|
+
<!-- src/App.svelte -->
|
|
162
|
+
<script lang="ts">
|
|
163
|
+
import Editor from '@motion-proto/live-tokens/editor';
|
|
164
|
+
</script>
|
|
165
|
+
|
|
166
|
+
<Editor />
|
|
167
|
+
```
|
|
168
|
+
|
|
130
169
|
```ts
|
|
131
|
-
|
|
132
|
-
import '
|
|
133
|
-
import '@
|
|
170
|
+
// vite.config.ts
|
|
171
|
+
import { defineConfig } from 'vite';
|
|
172
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
173
|
+
|
|
174
|
+
export default defineConfig({
|
|
175
|
+
plugins: [svelte()],
|
|
176
|
+
});
|
|
134
177
|
```
|
|
135
178
|
|
|
136
|
-
|
|
179
|
+
No `css: 'injected'` workaround, no `optimizeDeps` excludes — `vite build` works as-is. (You'll want the full `themeFileApi` plugin from the Quick install section above when you're ready to persist edits to disk.)
|
|
137
180
|
|
|
138
181
|
## Greenfield? Use the starter
|
|
139
182
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@motion-proto/live-tokens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 6/7.",
|
|
6
6
|
"keywords": [
|
|
@@ -29,11 +29,12 @@
|
|
|
29
29
|
"src/pages/ComponentEditorPage.svelte",
|
|
30
30
|
"src/pages/ComponentEditorPage.svelte.d.ts",
|
|
31
31
|
"src/styles/ui-editor.css",
|
|
32
|
-
"src/styles/form-controls.css",
|
|
32
|
+
"src/styles/ui-form-controls.css",
|
|
33
33
|
"src/styles/fonts.css",
|
|
34
34
|
"src/styles/fonts",
|
|
35
35
|
"src/styles/_padding.scss",
|
|
36
36
|
"src/styles/tokens.css",
|
|
37
|
+
"src/styles/site.css",
|
|
37
38
|
"src/data",
|
|
38
39
|
"src/assets",
|
|
39
40
|
"dist-plugin",
|
|
@@ -77,8 +78,9 @@
|
|
|
77
78
|
"require": "./dist-plugin/index.cjs"
|
|
78
79
|
},
|
|
79
80
|
"./styles/ui-editor.css": "./src/styles/ui-editor.css",
|
|
80
|
-
"./
|
|
81
|
-
"./
|
|
81
|
+
"./starter/tokens.css": "./src/styles/tokens.css",
|
|
82
|
+
"./starter/site.css": "./src/styles/site.css",
|
|
83
|
+
"./starter/fonts.css": "./src/styles/fonts.css"
|
|
82
84
|
},
|
|
83
85
|
"scripts": {
|
|
84
86
|
"dev": "vite",
|
|
@@ -90,7 +92,10 @@
|
|
|
90
92
|
"build:plugin": "tsup src/vite-plugin/index.ts --out-dir dist-plugin --format esm,cjs --dts --external vite --platform node --clean",
|
|
91
93
|
"build:lib": "npm run build:plugin",
|
|
92
94
|
"deploy:local": "bash scripts/deploy-local.sh",
|
|
93
|
-
"
|
|
95
|
+
"check:no-style-imports": "node scripts/check-no-style-imports.mjs",
|
|
96
|
+
"check:editor-font-isolation": "node scripts/check-editor-font-isolation.mjs",
|
|
97
|
+
"check:smoke-install": "bash scripts/smoke-install.sh",
|
|
98
|
+
"prepublishOnly": "npm run check:no-style-imports && npm run check:editor-font-isolation && npm run build:lib && npm run check:smoke-install"
|
|
94
99
|
},
|
|
95
100
|
"peerDependencies": {
|
|
96
101
|
"sass": "^1.0",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
|
|
118
118
|
<label>
|
|
119
119
|
<span>Cancel button (left)</span>
|
|
120
|
-
<select class="form-select" value={cancelVariant} onchange={setCancelVariant}>
|
|
120
|
+
<select class="ui-form-select" value={cancelVariant} onchange={setCancelVariant}>
|
|
121
121
|
{#each BUTTON_VARIANTS as v}
|
|
122
122
|
<option value={v}>{variantLabel(v)}</option>
|
|
123
123
|
{/each}
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
</label>
|
|
126
126
|
<label>
|
|
127
127
|
<span>Confirm button (right)</span>
|
|
128
|
-
<select class="form-select" value={confirmVariant} onchange={setConfirmVariant}>
|
|
128
|
+
<select class="ui-form-select" value={confirmVariant} onchange={setConfirmVariant}>
|
|
129
129
|
{#each BUTTON_VARIANTS as v}
|
|
130
130
|
<option value={v}>{variantLabel(v)}</option>
|
|
131
131
|
{/each}
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
</label>
|
|
123
123
|
<label>
|
|
124
124
|
<span>Right button</span>
|
|
125
|
-
<select class="form-select" bind:value={rightOption}>
|
|
125
|
+
<select class="ui-form-select" bind:value={rightOption}>
|
|
126
126
|
{#each BUTTON_VARIANT_OPTIONS as v}
|
|
127
127
|
<option value={v}>{variantLabel(v)}</option>
|
|
128
128
|
{/each}
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
</label>
|
|
131
131
|
<label>
|
|
132
132
|
<span>Left button</span>
|
|
133
|
-
<select class="form-select" bind:value={leftOption}>
|
|
133
|
+
<select class="ui-form-select" bind:value={leftOption}>
|
|
134
134
|
{#each BUTTON_VARIANT_OPTIONS as v}
|
|
135
135
|
<option value={v}>{variantLabel(v)}</option>
|
|
136
136
|
{/each}
|
|
@@ -499,7 +499,7 @@
|
|
|
499
499
|
|
|
500
500
|
.cfm-title-row {
|
|
501
501
|
display: flex;
|
|
502
|
-
align-items:
|
|
502
|
+
align-items: center;
|
|
503
503
|
gap: var(--ui-space-12);
|
|
504
504
|
flex-wrap: wrap;
|
|
505
505
|
}
|
|
@@ -518,6 +518,7 @@
|
|
|
518
518
|
display: inline-flex;
|
|
519
519
|
align-items: center;
|
|
520
520
|
gap: var(--ui-space-6);
|
|
521
|
+
margin-left: auto;
|
|
521
522
|
height: 26px;
|
|
522
523
|
padding: 0 14px;
|
|
523
524
|
font-size: var(--ui-font-size-xs);
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
import { componentRegistryEntries, validateRegistryAgainstServerScan } from '../component-editor/registry';
|
|
9
9
|
import { listComponents } from '../lib/componentConfigService';
|
|
10
10
|
import { selectedComponent } from '../lib/editorViewStore';
|
|
11
|
+
// Editor chrome + form controls + icon font must be JS imports (not @import
|
|
12
|
+
// inside the style block) so Vite resolves them via the module graph
|
|
13
|
+
// regardless of how the consumer compiles Svelte CSS (external ?lang.css vs
|
|
14
|
+
// injected); otherwise the @import URLs leak to the browser and 404 against
|
|
15
|
+
// the consumer's root.
|
|
16
|
+
import '../styles/ui-editor.css';
|
|
17
|
+
import '../styles/ui-form-controls.css';
|
|
18
|
+
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
11
19
|
|
|
12
20
|
let drawerOpen = $state(true);
|
|
13
21
|
|
|
@@ -167,7 +175,6 @@
|
|
|
167
175
|
</div>
|
|
168
176
|
|
|
169
177
|
<style>
|
|
170
|
-
@import '../styles/ui-editor.css';
|
|
171
178
|
.components-shell {
|
|
172
179
|
--rail-w: 48px;
|
|
173
180
|
display: grid;
|
package/src/pages/Editor.svelte
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
import { installEditorKeybindings } from '../lib/editorKeybindings';
|
|
6
6
|
import { initializeEditorStore } from '../lib/editorStore';
|
|
7
7
|
import { storageKey } from '../lib/editorConfig';
|
|
8
|
+
// Editor chrome + form controls + icon font must be JS imports (not @import
|
|
9
|
+
// inside the style block) so Vite resolves them via the module graph
|
|
10
|
+
// regardless of how the consumer compiles Svelte CSS (external ?lang.css vs
|
|
11
|
+
// injected); otherwise the @import URLs leak to the browser and 404 against
|
|
12
|
+
// the consumer's root.
|
|
13
|
+
import '../styles/ui-editor.css';
|
|
14
|
+
import '../styles/ui-form-controls.css';
|
|
15
|
+
import '@fortawesome/fontawesome-free/css/all.min.css';
|
|
8
16
|
|
|
9
17
|
const inOverlay = typeof window !== 'undefined' && window.parent !== window;
|
|
10
18
|
|
|
@@ -46,8 +54,6 @@
|
|
|
46
54
|
</div>
|
|
47
55
|
|
|
48
56
|
<style>
|
|
49
|
-
@import '../styles/ui-editor.css';
|
|
50
|
-
|
|
51
57
|
.editor-page {
|
|
52
58
|
min-height: 100vh;
|
|
53
59
|
background: black;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Site Styles — global typography for the themed pages
|
|
3
|
+
*
|
|
4
|
+
* Unscoped element selectors (h1, h2, p, a, ul li, …) consume the design
|
|
5
|
+
* tokens in tokens.css and recolor with the user's theme. Loaded globally
|
|
6
|
+
* from main.ts.
|
|
7
|
+
*
|
|
8
|
+
* Pair with: ui-editor.css (--ui-* chrome, opposite scope — neutral and
|
|
9
|
+
* theme-immune; only loaded on editor pages).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
h1 {
|
|
13
|
+
font-family: var(--font-display);
|
|
14
|
+
font-size: var(--font-size-4xl);
|
|
15
|
+
font-weight: var(--font-weight-semibold);
|
|
16
|
+
color: var(--text-primary);
|
|
17
|
+
margin: 0 0 var(--space-12);
|
|
18
|
+
line-height: var(--line-height-sm);
|
|
19
|
+
overflow-wrap: break-word;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
h2 {
|
|
23
|
+
font-family: var(--font-serif);
|
|
24
|
+
font-size: var(--font-size-2xl);
|
|
25
|
+
font-weight: var(--font-weight-semibold);
|
|
26
|
+
color: var(--text-primary);
|
|
27
|
+
letter-spacing: 0.01em;
|
|
28
|
+
margin: var(--space-32) 0 var(--space-12);
|
|
29
|
+
line-height: var(--line-height-sm);
|
|
30
|
+
overflow-wrap: break-word;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h3 {
|
|
34
|
+
font-family: var(--font-serif);
|
|
35
|
+
font-size: var(--font-size-xl);
|
|
36
|
+
font-weight: var(--font-weight-normal);
|
|
37
|
+
color: var(--text-primary);
|
|
38
|
+
margin: var(--space-24) 0 var(--space-8);
|
|
39
|
+
line-height: var(--line-height-sm);
|
|
40
|
+
overflow-wrap: break-word;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (max-width: 768px) {
|
|
44
|
+
h1 {
|
|
45
|
+
line-height: 1.1;
|
|
46
|
+
margin-bottom: var(--space-8);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
h2 {
|
|
50
|
+
line-height: 1.15;
|
|
51
|
+
margin-top: var(--space-24);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
h3 {
|
|
55
|
+
line-height: 1.2;
|
|
56
|
+
margin-top: var(--space-20);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
p {
|
|
61
|
+
font-family: var(--font-serif);
|
|
62
|
+
font-size: var(--font-size-md);
|
|
63
|
+
color: var(--text-secondary);
|
|
64
|
+
line-height: var(--line-height-md);
|
|
65
|
+
margin: 0 0 14px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
p:last-child {
|
|
69
|
+
margin-bottom: 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
a {
|
|
73
|
+
color: var(--text-brand);
|
|
74
|
+
text-decoration: none;
|
|
75
|
+
transition: color var(--duration-150);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
a:hover {
|
|
79
|
+
color: var(--color-brand-300);
|
|
80
|
+
text-decoration: underline;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
strong {
|
|
84
|
+
color: var(--text-primary);
|
|
85
|
+
font-weight: var(--font-weight-semibold);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ul {
|
|
89
|
+
padding-left: var(--space-24);
|
|
90
|
+
margin: var(--space-12) 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
ul li {
|
|
94
|
+
font-family: var(--font-serif);
|
|
95
|
+
font-size: var(--font-size-md);
|
|
96
|
+
color: var(--text-secondary);
|
|
97
|
+
line-height: 1.75;
|
|
98
|
+
margin-bottom: var(--space-4);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
ul li::marker {
|
|
102
|
+
color: var(--text-tertiary);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
ol {
|
|
106
|
+
padding-left: var(--space-24);
|
|
107
|
+
margin: var(--space-12) 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
ol li {
|
|
111
|
+
font-family: var(--font-sans);
|
|
112
|
+
font-size: var(--font-size-md);
|
|
113
|
+
color: var(--text-secondary);
|
|
114
|
+
line-height: 1.6;
|
|
115
|
+
margin-bottom: var(--space-4);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
ol li::marker {
|
|
119
|
+
color: var(--text-tertiary);
|
|
120
|
+
font-weight: var(--font-weight-semibold);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
hr {
|
|
124
|
+
border: none;
|
|
125
|
+
border-top: 1px solid var(--border-neutral-faint);
|
|
126
|
+
margin: var(--space-32) 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
blockquote {
|
|
130
|
+
margin: var(--space-16) 0;
|
|
131
|
+
padding: var(--space-12) var(--space-20);
|
|
132
|
+
border: 1px solid var(--color-brand-500);
|
|
133
|
+
border-left: 4px solid var(--color-brand-500);
|
|
134
|
+
background: var(--surface-neutral-high);
|
|
135
|
+
border-radius: 0 var(--radius-md) var(--radius-md) 0;
|
|
136
|
+
color: var(--text-secondary);
|
|
137
|
+
font-style: italic;
|
|
138
|
+
}
|
package/src/styles/tokens.css
CHANGED
|
@@ -1298,3 +1298,27 @@
|
|
|
1298
1298
|
--sectiondivider-special-description-font-weight: var(--font-weight-normal);
|
|
1299
1299
|
--sectiondivider-special-description-line-height: var(--line-height-md);
|
|
1300
1300
|
}
|
|
1301
|
+
|
|
1302
|
+
/* component-aliases:start */
|
|
1303
|
+
:root:root {
|
|
1304
|
+
/* button (default_01) */
|
|
1305
|
+
--button-primary-radius: var(--radius-full);
|
|
1306
|
+
--button-primary-hover-radius: var(--radius-full);
|
|
1307
|
+
--button-primary-disabled-radius: var(--radius-full);
|
|
1308
|
+
--button-secondary-radius: var(--radius-full);
|
|
1309
|
+
--button-secondary-hover-radius: var(--radius-full);
|
|
1310
|
+
--button-secondary-disabled-radius: var(--radius-full);
|
|
1311
|
+
--button-outline-radius: var(--radius-full);
|
|
1312
|
+
--button-outline-hover-radius: var(--radius-full);
|
|
1313
|
+
--button-outline-disabled-radius: var(--radius-full);
|
|
1314
|
+
--button-success-radius: var(--radius-full);
|
|
1315
|
+
--button-success-hover-radius: var(--radius-full);
|
|
1316
|
+
--button-success-disabled-radius: var(--radius-full);
|
|
1317
|
+
--button-danger-radius: var(--radius-full);
|
|
1318
|
+
--button-danger-hover-radius: var(--radius-full);
|
|
1319
|
+
--button-danger-disabled-radius: var(--radius-full);
|
|
1320
|
+
--button-warning-radius: var(--radius-full);
|
|
1321
|
+
--button-warning-hover-radius: var(--radius-full);
|
|
1322
|
+
--button-warning-disabled-radius: var(--radius-full);
|
|
1323
|
+
}
|
|
1324
|
+
/* component-aliases:end */
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/* Editor form controls — `--ui-*` tokens only. Theme-immune; see CONVENTIONS.md. */
|
|
2
|
+
|
|
3
|
+
/* ========================================
|
|
4
|
+
Form Field Layouts
|
|
5
|
+
======================================== */
|
|
6
|
+
|
|
7
|
+
/* Vertical Layout - Label Above Input/Select */
|
|
8
|
+
/* Usage: Add .ui-form-field-vertical to container div wrapping label + input/select */
|
|
9
|
+
.ui-form-field-vertical {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
align-items: stretch;
|
|
13
|
+
gap: var(--ui-space-4);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.ui-form-label {
|
|
17
|
+
margin-bottom: 0;
|
|
18
|
+
font-size: var(--ui-font-size-md);
|
|
19
|
+
color: var(--ui-text-primary);
|
|
20
|
+
font-weight: var(--ui-font-weight-normal);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Horizontal Layout - Label Beside Input/Select */
|
|
24
|
+
/* Usage: Add .ui-form-field-horizontal to container div wrapping label + input/select */
|
|
25
|
+
.ui-form-field-horizontal {
|
|
26
|
+
display: flex;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: var(--ui-space-8);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ui-form-field-horizontal .ui-form-label {
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Inline Layout - Label and Input/Select Close Together */
|
|
37
|
+
/* Usage: Add .ui-form-field-inline to container div wrapping label + input/select */
|
|
38
|
+
.ui-form-field-inline {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: var(--ui-space-12);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ui-form-field-inline .ui-form-label {
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
flex-shrink: 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ui-form-field-inline .ui-form-select,
|
|
50
|
+
.ui-form-field-inline .ui-form-input {
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* ========================================
|
|
55
|
+
Form Control Styling
|
|
56
|
+
======================================== */
|
|
57
|
+
|
|
58
|
+
/* Select/Dropdown Styling */
|
|
59
|
+
.ui-form-select {
|
|
60
|
+
/* No vertical padding - let min-height and line-height center text naturally */
|
|
61
|
+
padding: 0 var(--ui-space-16);
|
|
62
|
+
min-height: 2.375rem; /* ~38px to match button height */
|
|
63
|
+
background: var(--ui-surface-lowest) !important;
|
|
64
|
+
border: 1px solid var(--ui-border-default) !important;
|
|
65
|
+
border-radius: var(--ui-radius-md);
|
|
66
|
+
color: var(--ui-text-primary) !important;
|
|
67
|
+
font-family: var(--ui-font-sans);
|
|
68
|
+
font-size: var(--ui-font-size-md);
|
|
69
|
+
font-weight: var(--ui-font-weight-normal);
|
|
70
|
+
line-height: var(--ui-line-height-normal);
|
|
71
|
+
vertical-align: middle;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
transition: all var(--ui-transition-fast);
|
|
74
|
+
/* Prevent clipping */
|
|
75
|
+
overflow: visible !important;
|
|
76
|
+
box-sizing: border-box !important;
|
|
77
|
+
/* Reset browser defaults */
|
|
78
|
+
-webkit-appearance: none;
|
|
79
|
+
-moz-appearance: none;
|
|
80
|
+
appearance: none;
|
|
81
|
+
/* Custom dropdown arrow */
|
|
82
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M6 8L1 3h10z'/%3E%3C/svg%3E") !important;
|
|
83
|
+
background-repeat: no-repeat !important;
|
|
84
|
+
background-position: right var(--ui-space-12) center !important;
|
|
85
|
+
padding-right: var(--ui-space-32) !important;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ui-form-select:hover:not(:disabled) {
|
|
89
|
+
background-color: var(--ui-surface-low) !important;
|
|
90
|
+
border-color: var(--ui-border-medium) !important;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ui-form-select:focus {
|
|
94
|
+
outline: none;
|
|
95
|
+
border-color: var(--ui-border-strong) !important;
|
|
96
|
+
box-shadow: 0 0 0 2px hsla(0, 58%, 50%, 0.2);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.ui-form-select:focus-visible {
|
|
100
|
+
outline: 2px solid var(--ui-highlight);
|
|
101
|
+
outline-offset: 2px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.ui-form-select:active:not(:disabled) {
|
|
105
|
+
background-color: var(--ui-surface) !important;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.ui-form-select:disabled {
|
|
109
|
+
background-color: var(--ui-surface-lowest) !important;
|
|
110
|
+
border-color: var(--ui-border-faint) !important;
|
|
111
|
+
color: var(--ui-text-disabled) !important;
|
|
112
|
+
cursor: not-allowed;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Option Styling - Critical for Legibility */
|
|
116
|
+
/* Note: Most option styling is controlled by browser/OS and cannot be fully overridden */
|
|
117
|
+
/* These styles apply where browsers allow (limited support in Chrome/Firefox) */
|
|
118
|
+
.ui-form-select option {
|
|
119
|
+
background-color: var(--ui-surface-lowest) !important;
|
|
120
|
+
color: var(--ui-text-primary) !important;
|
|
121
|
+
padding: var(--ui-space-8) var(--ui-space-12);
|
|
122
|
+
min-height: 2rem;
|
|
123
|
+
font-size: var(--ui-font-size-md);
|
|
124
|
+
font-family: var(--ui-font-sans);
|
|
125
|
+
line-height: var(--ui-line-height-normal);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Disabled options */
|
|
129
|
+
.ui-form-select option:disabled {
|
|
130
|
+
color: var(--ui-text-disabled) !important;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* Input Field Styling */
|
|
134
|
+
.ui-form-input {
|
|
135
|
+
padding: var(--ui-space-8);
|
|
136
|
+
background: var(--ui-surface-lowest);
|
|
137
|
+
border: 1px solid var(--ui-border-default);
|
|
138
|
+
border-radius: var(--ui-radius-md);
|
|
139
|
+
color: var(--ui-text-primary);
|
|
140
|
+
font-family: var(--ui-font-sans);
|
|
141
|
+
font-size: var(--ui-font-size-md);
|
|
142
|
+
transition: border-color var(--ui-transition-fast);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.ui-form-input:hover:not(:disabled) {
|
|
146
|
+
border-color: var(--ui-border-strong);
|
|
147
|
+
background: var(--ui-surface-low);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.ui-form-input:focus {
|
|
151
|
+
outline: none;
|
|
152
|
+
border-color: var(--ui-border-strong);
|
|
153
|
+
box-shadow: 0 0 0 3px hsla(240, 5%, 38%, 0.2);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.ui-form-input:disabled {
|
|
157
|
+
background: var(--ui-surface-lowest);
|
|
158
|
+
border-color: var(--ui-border-faint);
|
|
159
|
+
color: var(--ui-text-disabled);
|
|
160
|
+
cursor: not-allowed;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Placeholder text styling */
|
|
164
|
+
.ui-form-input::placeholder {
|
|
165
|
+
color: var(--ui-text-muted);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Number input spinner buttons */
|
|
169
|
+
.ui-form-input[type="number"]::-webkit-inner-spin-button,
|
|
170
|
+
.ui-form-input[type="number"]::-webkit-outer-spin-button {
|
|
171
|
+
opacity: 1;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Checkbox and Radio Styling */
|
|
175
|
+
.ui-form-checkbox,
|
|
176
|
+
.ui-form-radio {
|
|
177
|
+
cursor: pointer;
|
|
178
|
+
width: var(--ui-space-16);
|
|
179
|
+
height: var(--ui-space-16);
|
|
180
|
+
accent-color: var(--ui-gray-600);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.ui-form-checkbox:disabled,
|
|
184
|
+
.ui-form-radio:disabled {
|
|
185
|
+
cursor: not-allowed;
|
|
186
|
+
}
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
style="font-family: {slotCssValue(slot)};{stack.variable === '--font-display' ? ' font-size: var(--ui-font-size-2xl);' : ''}"
|
|
190
190
|
>The quick brown fox jumps over the lazy dog</span>
|
|
191
191
|
<select
|
|
192
|
-
class="form-select slot-select"
|
|
192
|
+
class="ui-form-select slot-select"
|
|
193
193
|
value={slotKey(slot)}
|
|
194
194
|
onchange={(e) => onSelectChange(e, stack.variable, i)}
|
|
195
195
|
>
|
|
@@ -252,7 +252,7 @@
|
|
|
252
252
|
{#if addMode === 'google'}
|
|
253
253
|
<input
|
|
254
254
|
type="text"
|
|
255
|
-
class="form-input"
|
|
255
|
+
class="ui-form-input"
|
|
256
256
|
placeholder="Search Google Fonts (e.g. Inter)"
|
|
257
257
|
bind:value={googleQuery}
|
|
258
258
|
/>
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
{:else if addMode === 'url'}
|
|
272
272
|
<input
|
|
273
273
|
type="text"
|
|
274
|
-
class="form-input"
|
|
274
|
+
class="ui-form-input"
|
|
275
275
|
placeholder="https://fonts.googleapis.com/css2?family=... or Typekit URL"
|
|
276
276
|
bind:value={urlInput}
|
|
277
277
|
/>
|
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
<div class="pf-detected">Couldn't auto-detect families (CORS or no metadata). Name them:</div>
|
|
311
311
|
<input
|
|
312
312
|
type="text"
|
|
313
|
-
class="form-input"
|
|
313
|
+
class="ui-form-input"
|
|
314
314
|
placeholder="Comma-separated family names"
|
|
315
315
|
bind:value={urlManualFamilies}
|
|
316
316
|
/>
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
{/if}
|
|
319
319
|
{:else if addMode === 'fontface'}
|
|
320
320
|
<textarea
|
|
321
|
-
class="form-input pf-textarea"
|
|
321
|
+
class="ui-form-input pf-textarea"
|
|
322
322
|
placeholder={'Paste one or more @font-face { ... } rules'}
|
|
323
323
|
rows="6"
|
|
324
324
|
bind:value={fontFaceText}
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/* Form Controls - Global Styling for Dropdowns, Selects, Inputs */
|
|
2
|
-
/* Ensures consistent, legible styling across the application */
|
|
3
|
-
|
|
4
|
-
/* ========================================
|
|
5
|
-
Form Field Layouts
|
|
6
|
-
======================================== */
|
|
7
|
-
|
|
8
|
-
/* Vertical Layout - Label Above Input/Select */
|
|
9
|
-
/* Usage: Add .form-field-vertical to container div wrapping label + input/select */
|
|
10
|
-
.form-field-vertical {
|
|
11
|
-
display: flex;
|
|
12
|
-
flex-direction: column;
|
|
13
|
-
align-items: stretch;
|
|
14
|
-
gap: var(--space-4);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.form-label {
|
|
18
|
-
margin-bottom: 0;
|
|
19
|
-
font-size: var(--font-size-md);
|
|
20
|
-
color: var(--text-primary);
|
|
21
|
-
font-weight: var(--font-weight-light);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/* Horizontal Layout - Label Beside Input/Select */
|
|
25
|
-
/* Usage: Add .form-field-horizontal to container div wrapping label + input/select */
|
|
26
|
-
.form-field-horizontal {
|
|
27
|
-
display: flex;
|
|
28
|
-
justify-content: space-between;
|
|
29
|
-
align-items: center;
|
|
30
|
-
gap: var(--space-8);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.form-field-horizontal .form-label {
|
|
34
|
-
white-space: nowrap;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* Inline Layout - Label and Input/Select Close Together */
|
|
38
|
-
/* Usage: Add .form-field-inline to container div wrapping label + input/select */
|
|
39
|
-
.form-field-inline {
|
|
40
|
-
display: flex;
|
|
41
|
-
align-items: center;
|
|
42
|
-
gap: var(--space-12);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.form-field-inline .form-label {
|
|
46
|
-
white-space: nowrap;
|
|
47
|
-
flex-shrink: 0;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.form-field-inline .form-select,
|
|
51
|
-
.form-field-inline .form-input {
|
|
52
|
-
flex-shrink: 0;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* ========================================
|
|
56
|
-
Form Control Styling
|
|
57
|
-
======================================== */
|
|
58
|
-
|
|
59
|
-
/* Select/Dropdown Styling - Design System Compliant */
|
|
60
|
-
.form-select {
|
|
61
|
-
/* Base styling using design system colors */
|
|
62
|
-
/* No vertical padding - let min-height and line-height center text naturally */
|
|
63
|
-
padding: 0 var(--space-16);
|
|
64
|
-
min-height: 2.375rem; /* ~38px to match button height */
|
|
65
|
-
background: var(--surface-neutral-lowest) !important;
|
|
66
|
-
border: 1px solid var(--border-neutral) !important;
|
|
67
|
-
border-radius: var(--radius-md);
|
|
68
|
-
color: var(--text-primary) !important;
|
|
69
|
-
font-family: var(--font-sans);
|
|
70
|
-
font-size: var(--font-size-md);
|
|
71
|
-
font-weight: var(--font-weight-light);
|
|
72
|
-
line-height: var(--line-height-md);
|
|
73
|
-
vertical-align: middle;
|
|
74
|
-
cursor: pointer;
|
|
75
|
-
transition: all var(--duration-150);
|
|
76
|
-
/* Prevent clipping */
|
|
77
|
-
overflow: visible !important;
|
|
78
|
-
box-sizing: border-box !important;
|
|
79
|
-
/* Reset browser defaults */
|
|
80
|
-
-webkit-appearance: none;
|
|
81
|
-
-moz-appearance: none;
|
|
82
|
-
appearance: none;
|
|
83
|
-
/* Custom dropdown arrow */
|
|
84
|
-
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M6 8L1 3h10z'/%3E%3C/svg%3E") !important;
|
|
85
|
-
background-repeat: no-repeat !important;
|
|
86
|
-
background-position: right var(--space-12) center !important;
|
|
87
|
-
padding-right: var(--space-32) !important;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.form-select:hover:not(:disabled) {
|
|
91
|
-
background-color: var(--surface-neutral-low) !important;
|
|
92
|
-
border-color: var(--border-neutral-medium) !important;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.form-select:focus {
|
|
96
|
-
outline: none;
|
|
97
|
-
border-color: var(--border-neutral-strong) !important;
|
|
98
|
-
box-shadow: 0 0 0 2px hsla(0, 58%, 50%, 0.2);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.form-select:focus-visible {
|
|
102
|
-
outline: 2px solid var(--color-brand-500);
|
|
103
|
-
outline-offset: 2px;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.form-select:active:not(:disabled) {
|
|
107
|
-
background-color: var(--surface-neutral) !important;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.form-select:disabled {
|
|
111
|
-
background-color: var(--surface-neutral-lowest) !important;
|
|
112
|
-
border-color: var(--border-neutral-faint) !important;
|
|
113
|
-
color: var(--text-disabled) !important;
|
|
114
|
-
cursor: not-allowed;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/* Option Styling - Critical for Legibility */
|
|
118
|
-
/* Note: Most option styling is controlled by browser/OS and cannot be fully overridden */
|
|
119
|
-
/* These styles apply where browsers allow (limited support in Chrome/Firefox) */
|
|
120
|
-
.form-select option {
|
|
121
|
-
background-color: var(--surface-neutral-lowest) !important;
|
|
122
|
-
color: var(--text-primary) !important;
|
|
123
|
-
padding: var(--space-8) var(--space-12);
|
|
124
|
-
min-height: 2rem;
|
|
125
|
-
font-size: var(--font-size-md);
|
|
126
|
-
font-family: var(--font-sans);
|
|
127
|
-
line-height: var(--line-height-md);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/* Disabled options */
|
|
131
|
-
.form-select option:disabled {
|
|
132
|
-
color: var(--text-disabled) !important;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/* Input Field Styling */
|
|
136
|
-
.form-input {
|
|
137
|
-
padding: var(--space-8);
|
|
138
|
-
background: var(--surface-neutral-lowest);
|
|
139
|
-
border: 1px solid var(--border-neutral);
|
|
140
|
-
border-radius: var(--radius-md);
|
|
141
|
-
color: var(--text-primary);
|
|
142
|
-
font-family: var(--font-sans);
|
|
143
|
-
font-size: var(--font-size-md);
|
|
144
|
-
transition: border-color var(--duration-150);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.form-input:hover:not(:disabled) {
|
|
148
|
-
border-color: var(--border-neutral-strong);
|
|
149
|
-
background: var(--surface-neutral-low);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.form-input:focus {
|
|
153
|
-
outline: none;
|
|
154
|
-
border-color: var(--border-neutral-strong);
|
|
155
|
-
box-shadow: 0 0 0 3px hsla(240, 5%, 38%, 0.2);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
.form-input:disabled {
|
|
159
|
-
background: var(--surface-neutral-lowest);
|
|
160
|
-
border-color: var(--border-neutral-faint);
|
|
161
|
-
color: var(--text-disabled);
|
|
162
|
-
cursor: not-allowed;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/* Placeholder text styling */
|
|
166
|
-
.form-input::placeholder {
|
|
167
|
-
color: var(--text-muted);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/* Number input spinner buttons */
|
|
171
|
-
.form-input[type="number"]::-webkit-inner-spin-button,
|
|
172
|
-
.form-input[type="number"]::-webkit-outer-spin-button {
|
|
173
|
-
opacity: 1;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/* Checkbox and Radio Styling */
|
|
177
|
-
.form-checkbox,
|
|
178
|
-
.form-radio {
|
|
179
|
-
cursor: pointer;
|
|
180
|
-
width: var(--space-16);
|
|
181
|
-
height: var(--space-16);
|
|
182
|
-
accent-color: var(--color-neutral-400);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.form-checkbox:disabled,
|
|
186
|
-
.form-radio:disabled {
|
|
187
|
-
cursor: not-allowed;
|
|
188
|
-
}
|