@pya-platform/tokens 0.1.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/CHANGELOG.md +9 -0
- package/package.json +26 -0
- package/src/base.css +151 -0
- package/src/components.css +738 -0
- package/src/glass.css +26 -0
- package/src/index.css +22 -0
- package/src/motion.css +22 -0
- package/src/palettes/caramelo.css +8 -0
- package/src/palettes/lima.css +8 -0
- package/src/palettes/mango.css +43 -0
- package/src/palettes/miel.css +8 -0
- package/src/palettes/tomate.css +8 -0
- package/src/themes/claro.css +26 -0
- package/src/themes/oscuro.css +39 -0
- package/src/ts/tokens.ts +51 -0
- package/src/typography.css +30 -0
- package/tsconfig.json +9 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @pya/tokens
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a9ca6bf: Initial release of the Pya platform packages. Extracted from `pyaeats-app`, consumed by `pyaeats-app` (food delivery) and `pyaserv` (services classifieds).
|
|
8
|
+
|
|
9
|
+
Each package exposes a Hono router factory (auth/cms/reviews/comments) or a typed helper (email/audit/cf) parameterised over Cloudflare D1 + KV bindings. UI primitives ship as Lit web components on top of `@pya/tokens` (CSS custom properties). See `ROADMAP.md` and `docs/phase-6-rollout.md` for the consumer cutover plan.
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pya-platform/tokens",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"registry": "https://registry.npmjs.org",
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/undeadliner/pya-platform.git"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"description": "Design tokens for Pya projects — CSS custom properties for color, motion, spacing, typography. Palettes + themes ship as separate CSS files.",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": "./src/ts/tokens.ts",
|
|
17
|
+
"./index.css": "./src/index.css",
|
|
18
|
+
"./*.css": "./src/*.css",
|
|
19
|
+
"./palettes/*.css": "./src/palettes/*.css",
|
|
20
|
+
"./themes/*.css": "./src/themes/*.css"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"generate-ts": "bun ./scripts/generate-ts.ts",
|
|
24
|
+
"type-check": "tsc --noEmit"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/base.css
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
/* Shape */
|
|
3
|
+
--pya-radius-sm: 10px;
|
|
4
|
+
--pya-radius-md: 16px;
|
|
5
|
+
--pya-radius-lg: 22px;
|
|
6
|
+
--pya-radius-pill: 999px;
|
|
7
|
+
|
|
8
|
+
/* Spacing scale (4px grid) */
|
|
9
|
+
--pya-space-1: 4px;
|
|
10
|
+
--pya-space-2: 8px;
|
|
11
|
+
--pya-space-3: 12px;
|
|
12
|
+
--pya-space-4: 16px;
|
|
13
|
+
--pya-space-5: 24px;
|
|
14
|
+
--pya-space-6: 32px;
|
|
15
|
+
--pya-space-7: 48px;
|
|
16
|
+
--pya-space-8: 64px;
|
|
17
|
+
|
|
18
|
+
/* Containers */
|
|
19
|
+
--pya-content-narrow: 720px;
|
|
20
|
+
--pya-content-wide: 1080px;
|
|
21
|
+
--pya-content-bleed: 1280px;
|
|
22
|
+
|
|
23
|
+
/* Focus ring — keyboard-only, soft glow.
|
|
24
|
+
AAA: ≥3px effective width at ≥3:1 contrast (WCAG 2.4.11/2.4.13).
|
|
25
|
+
We render it as a box-shadow halo, NOT outline — outline + offset reads
|
|
26
|
+
as "dev-tools selected element" and feels aggressive. */
|
|
27
|
+
--pya-focus-ring-width: 3px;
|
|
28
|
+
--pya-focus-ring-color: #0057b8;
|
|
29
|
+
--pya-focus-ring-offset: 0px;
|
|
30
|
+
|
|
31
|
+
/* Target sizes — AAA 2.5.5: at least 44×44 CSS px. */
|
|
32
|
+
--pya-target-min: 44px;
|
|
33
|
+
--pya-target-comfortable: 48px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
*,
|
|
37
|
+
*::before,
|
|
38
|
+
*::after {
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
html {
|
|
43
|
+
-webkit-text-size-adjust: 100%;
|
|
44
|
+
text-size-adjust: 100%;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
body {
|
|
48
|
+
margin: 0;
|
|
49
|
+
background: var(--pya-surface-page, #fff7f0);
|
|
50
|
+
color: var(--pya-text, #20140a);
|
|
51
|
+
font-family: var(--pya-font-primary, system-ui, sans-serif);
|
|
52
|
+
line-height: 1.5;
|
|
53
|
+
-webkit-font-smoothing: antialiased;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
img,
|
|
57
|
+
svg {
|
|
58
|
+
max-width: 100%;
|
|
59
|
+
display: block;
|
|
60
|
+
}
|
|
61
|
+
button {
|
|
62
|
+
font: inherit;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
border: none;
|
|
65
|
+
background: none;
|
|
66
|
+
color: inherit;
|
|
67
|
+
}
|
|
68
|
+
a {
|
|
69
|
+
color: inherit;
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* iOS auto-zooms text-entry inputs when font-size < 16 px — but checkbox /
|
|
74
|
+
radio / file / range / color have intrinsic sizes that bloat under 16 px.
|
|
75
|
+
Exclude them. Low specificity (:where) so component CSS overrides. */
|
|
76
|
+
:where(
|
|
77
|
+
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not(
|
|
78
|
+
[type="file"]
|
|
79
|
+
):not([type="image"]):not([type="reset"]):not([type="submit"]):not([type="button"]),
|
|
80
|
+
select,
|
|
81
|
+
textarea
|
|
82
|
+
) {
|
|
83
|
+
font-size: 16px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Only true interactive elements get the focus ring — no universal rule.
|
|
87
|
+
The :where() wrapper keeps specificity at 0 so component CSS can override. */
|
|
88
|
+
:where(
|
|
89
|
+
a,
|
|
90
|
+
button,
|
|
91
|
+
[role="button"],
|
|
92
|
+
input,
|
|
93
|
+
select,
|
|
94
|
+
textarea,
|
|
95
|
+
summary,
|
|
96
|
+
[tabindex]:not([tabindex="-1"])
|
|
97
|
+
):focus-visible {
|
|
98
|
+
outline: none;
|
|
99
|
+
box-shadow: 0 0 0 var(--pya-focus-ring-width)
|
|
100
|
+
color-mix(in srgb, var(--pya-focus-ring-color) 88%, transparent);
|
|
101
|
+
transition: box-shadow var(--pya-motion-fast, 140ms) var(--pya-ease-standard, ease-out);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Chromium sometimes flags a clicked link/button as focus-visible after the
|
|
105
|
+
click resolves. Hard-suppress when the element is being actively pressed,
|
|
106
|
+
so the ring never lingers from a mouse interaction. */
|
|
107
|
+
:focus:not(:focus-visible) {
|
|
108
|
+
outline: none;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* No mobile tap-flash on interactive surfaces. */
|
|
112
|
+
:where(a, button, [role="button"], summary, label, [tabindex]:not([tabindex="-1"])) {
|
|
113
|
+
-webkit-tap-highlight-color: transparent;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* No accidental text selection on click/double-click for things the user
|
|
117
|
+
actually presses. Keyboard focus still surfaces via :focus-visible above.
|
|
118
|
+
Inputs, textareas, paragraphs etc. stay user-selectable by default. */
|
|
119
|
+
:where(button, [role="button"], summary, .pya-link-button) {
|
|
120
|
+
user-select: none;
|
|
121
|
+
-webkit-user-select: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Visually hidden but available to screen readers. */
|
|
125
|
+
.visually-hidden {
|
|
126
|
+
position: absolute;
|
|
127
|
+
width: 1px;
|
|
128
|
+
height: 1px;
|
|
129
|
+
padding: 0;
|
|
130
|
+
margin: -1px;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
clip: rect(0 0 0 0);
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
border: 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Skip link — first focusable in <body>; visible on focus. */
|
|
138
|
+
.skip-link {
|
|
139
|
+
position: absolute;
|
|
140
|
+
top: 0;
|
|
141
|
+
left: 0;
|
|
142
|
+
background: var(--pya-surface, #fff);
|
|
143
|
+
color: var(--pya-text, #000);
|
|
144
|
+
padding: var(--pya-space-2) var(--pya-space-3);
|
|
145
|
+
z-index: 1000;
|
|
146
|
+
transform: translateY(-100%);
|
|
147
|
+
transition: transform var(--pya-motion-fast) var(--pya-ease-standard);
|
|
148
|
+
}
|
|
149
|
+
.skip-link:focus-visible {
|
|
150
|
+
transform: translateY(0);
|
|
151
|
+
}
|
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
/* PyaEats design-system component classes.
|
|
2
|
+
Each class is self-contained — full CSS body, no utility composition.
|
|
3
|
+
Tokens (--pya-space-*, --pya-radius-*, --pya-text-*) are the only shared layer. */
|
|
4
|
+
|
|
5
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
6
|
+
LINK / TEXT primitives. */
|
|
7
|
+
.pya-back-link {
|
|
8
|
+
color: var(--pya-text);
|
|
9
|
+
text-decoration: none;
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
gap: var(--pya-space-2);
|
|
13
|
+
}
|
|
14
|
+
.pya-back-link:hover {
|
|
15
|
+
text-decoration: underline;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.pya-link-button {
|
|
19
|
+
background: none;
|
|
20
|
+
border: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
margin: 0;
|
|
23
|
+
color: var(--pya-text);
|
|
24
|
+
text-decoration: underline;
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
font: inherit;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.pya-icon-button {
|
|
30
|
+
background: none;
|
|
31
|
+
border: 1px solid var(--pya-border);
|
|
32
|
+
border-radius: var(--pya-radius-pill);
|
|
33
|
+
padding: 6px 10px;
|
|
34
|
+
min-height: var(--pya-target-min);
|
|
35
|
+
min-width: 44px;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
color: var(--pya-text);
|
|
38
|
+
font: inherit;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.pya-muted {
|
|
42
|
+
color: var(--pya-text-muted);
|
|
43
|
+
font-size: var(--pya-font-size-sm);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.pya-text-error {
|
|
47
|
+
color: #c0392b;
|
|
48
|
+
}
|
|
49
|
+
:root[data-theme="oscuro"] .pya-text-error {
|
|
50
|
+
color: #fca5a5;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.pya-code-mono {
|
|
54
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
58
|
+
PAGE LAYOUT — containers, sections, page-titles. */
|
|
59
|
+
.pya-container {
|
|
60
|
+
width: 100%;
|
|
61
|
+
max-width: var(--pya-content-wide);
|
|
62
|
+
margin-inline: auto;
|
|
63
|
+
padding-inline: var(--pya-space-4);
|
|
64
|
+
}
|
|
65
|
+
.pya-container--narrow {
|
|
66
|
+
max-width: var(--pya-content-narrow);
|
|
67
|
+
}
|
|
68
|
+
.pya-container--540 {
|
|
69
|
+
max-width: 540px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.pya-section {
|
|
73
|
+
padding-block: var(--pya-space-5);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
77
|
+
CARDS — generic surface for list-items / detail panels. */
|
|
78
|
+
.pya-card {
|
|
79
|
+
display: block;
|
|
80
|
+
padding: var(--pya-space-4);
|
|
81
|
+
border: 1px solid var(--pya-border);
|
|
82
|
+
border-radius: var(--pya-radius-md);
|
|
83
|
+
background: var(--pya-surface);
|
|
84
|
+
color: inherit;
|
|
85
|
+
text-decoration: none;
|
|
86
|
+
box-sizing: border-box;
|
|
87
|
+
}
|
|
88
|
+
.pya-card--dashed {
|
|
89
|
+
border-style: dashed;
|
|
90
|
+
}
|
|
91
|
+
.pya-card--empty-state {
|
|
92
|
+
padding: var(--pya-space-4);
|
|
93
|
+
text-align: center;
|
|
94
|
+
color: var(--pya-text-muted);
|
|
95
|
+
border: 1px dashed var(--pya-border);
|
|
96
|
+
border-radius: var(--pya-radius-md);
|
|
97
|
+
}
|
|
98
|
+
.pya-card--empty-state p {
|
|
99
|
+
margin: 0 0 var(--pya-space-3);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.pya-list-card {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: var(--pya-space-3);
|
|
106
|
+
padding: var(--pya-space-4);
|
|
107
|
+
border: 1px solid var(--pya-border);
|
|
108
|
+
border-radius: var(--pya-radius-md);
|
|
109
|
+
background: var(--pya-surface);
|
|
110
|
+
color: inherit;
|
|
111
|
+
text-decoration: none;
|
|
112
|
+
min-height: 76px;
|
|
113
|
+
box-sizing: border-box;
|
|
114
|
+
}
|
|
115
|
+
.pya-list-card:hover {
|
|
116
|
+
border-color: var(--pya-acc);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
120
|
+
FORMS — fields, labels, inputs, fieldsets, error rows. */
|
|
121
|
+
.pya-field {
|
|
122
|
+
display: grid;
|
|
123
|
+
gap: var(--pya-space-2);
|
|
124
|
+
}
|
|
125
|
+
.pya-field__label {
|
|
126
|
+
font-weight: 600;
|
|
127
|
+
}
|
|
128
|
+
.pya-field__hint {
|
|
129
|
+
color: var(--pya-text-muted);
|
|
130
|
+
font-weight: 400;
|
|
131
|
+
font-size: var(--pya-font-size-sm);
|
|
132
|
+
}
|
|
133
|
+
.pya-field__error {
|
|
134
|
+
color: #c0392b;
|
|
135
|
+
min-height: 1.25em;
|
|
136
|
+
margin: 0;
|
|
137
|
+
font-size: var(--pya-font-size-sm);
|
|
138
|
+
}
|
|
139
|
+
:root[data-theme="oscuro"] .pya-field__error {
|
|
140
|
+
color: #fca5a5;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.pya-input,
|
|
144
|
+
.pya-select,
|
|
145
|
+
.pya-textarea {
|
|
146
|
+
display: block;
|
|
147
|
+
width: 100%;
|
|
148
|
+
padding: var(--pya-space-3);
|
|
149
|
+
border: 1px solid var(--pya-border);
|
|
150
|
+
border-radius: var(--pya-radius-sm);
|
|
151
|
+
background: var(--pya-surface);
|
|
152
|
+
color: var(--pya-text);
|
|
153
|
+
font: inherit;
|
|
154
|
+
box-sizing: border-box;
|
|
155
|
+
/* Mobile: 16 px font suppresses iOS auto-zoom; 44 px min target. */
|
|
156
|
+
font-size: 16px;
|
|
157
|
+
min-height: 44px;
|
|
158
|
+
}
|
|
159
|
+
.pya-input--lg,
|
|
160
|
+
.pya-select--lg {
|
|
161
|
+
min-height: var(--pya-target-min);
|
|
162
|
+
}
|
|
163
|
+
.pya-input--otp {
|
|
164
|
+
font-size: var(--pya-font-size-lg);
|
|
165
|
+
letter-spacing: 4px;
|
|
166
|
+
text-align: center;
|
|
167
|
+
font-variant-numeric: tabular-nums;
|
|
168
|
+
min-height: var(--pya-target-min);
|
|
169
|
+
}
|
|
170
|
+
.pya-textarea {
|
|
171
|
+
min-height: 280px;
|
|
172
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
173
|
+
}
|
|
174
|
+
.pya-textarea--inline {
|
|
175
|
+
min-height: auto;
|
|
176
|
+
font-family: inherit;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.pya-fieldset {
|
|
180
|
+
border: 1px solid var(--pya-border);
|
|
181
|
+
border-radius: var(--pya-radius-md);
|
|
182
|
+
padding: var(--pya-space-4);
|
|
183
|
+
}
|
|
184
|
+
.pya-fieldset__legend {
|
|
185
|
+
padding: 0 var(--pya-space-2);
|
|
186
|
+
font-weight: 700;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.pya-checkbox-row {
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
gap: var(--pya-space-3);
|
|
193
|
+
padding: var(--pya-space-3);
|
|
194
|
+
border: 1px dashed var(--pya-border);
|
|
195
|
+
border-radius: var(--pya-radius-md);
|
|
196
|
+
}
|
|
197
|
+
.pya-checkbox-row__box {
|
|
198
|
+
width: 20px;
|
|
199
|
+
height: 20px;
|
|
200
|
+
flex: none;
|
|
201
|
+
}
|
|
202
|
+
.pya-checkbox-row__title {
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
}
|
|
205
|
+
.pya-checkbox-row__hint {
|
|
206
|
+
color: var(--pya-text-muted);
|
|
207
|
+
font-size: var(--pya-font-size-sm);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Form-level layout. */
|
|
211
|
+
.pya-form {
|
|
212
|
+
display: grid;
|
|
213
|
+
gap: var(--pya-space-4);
|
|
214
|
+
max-width: var(--pya-content-narrow);
|
|
215
|
+
}
|
|
216
|
+
.pya-form--560 {
|
|
217
|
+
max-width: 560px;
|
|
218
|
+
}
|
|
219
|
+
.pya-form--640 {
|
|
220
|
+
max-width: 640px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.pya-form-grid {
|
|
224
|
+
display: grid;
|
|
225
|
+
gap: var(--pya-space-3);
|
|
226
|
+
}
|
|
227
|
+
.pya-form-grid--2-1 {
|
|
228
|
+
grid-template-columns: 2fr 1fr;
|
|
229
|
+
}
|
|
230
|
+
.pya-form-grid--1-1 {
|
|
231
|
+
grid-template-columns: 1fr 1fr;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.pya-form-actions {
|
|
235
|
+
display: flex;
|
|
236
|
+
gap: var(--pya-space-3);
|
|
237
|
+
align-items: center;
|
|
238
|
+
flex-wrap: wrap;
|
|
239
|
+
}
|
|
240
|
+
.pya-form-actions--centered {
|
|
241
|
+
justify-content: center;
|
|
242
|
+
}
|
|
243
|
+
.pya-form-actions__status {
|
|
244
|
+
color: var(--pya-text-muted);
|
|
245
|
+
font-size: var(--pya-font-size-sm);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Button helpers that don't conflict with @pyaeats/ui buttons. */
|
|
249
|
+
.pya-button--block {
|
|
250
|
+
width: 100%;
|
|
251
|
+
}
|
|
252
|
+
.pya-button--centered {
|
|
253
|
+
text-align: center;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
257
|
+
BANNERS — info / warning / error callouts. */
|
|
258
|
+
.pya-banner {
|
|
259
|
+
margin: var(--pya-space-4) 0 var(--pya-space-5);
|
|
260
|
+
padding: var(--pya-space-4);
|
|
261
|
+
border-radius: var(--pya-radius-md);
|
|
262
|
+
border-left: 4px solid currentColor;
|
|
263
|
+
background: var(--pya-surface-2);
|
|
264
|
+
color: var(--pya-text);
|
|
265
|
+
}
|
|
266
|
+
.pya-banner h2 {
|
|
267
|
+
margin: 0 0 var(--pya-space-2);
|
|
268
|
+
font-size: var(--pya-font-size-md);
|
|
269
|
+
}
|
|
270
|
+
.pya-banner p {
|
|
271
|
+
margin: 0 0 var(--pya-space-3);
|
|
272
|
+
}
|
|
273
|
+
.pya-banner ul {
|
|
274
|
+
margin: 0 0 var(--pya-space-3);
|
|
275
|
+
padding-left: var(--pya-space-5);
|
|
276
|
+
}
|
|
277
|
+
.pya-banner__cta {
|
|
278
|
+
display: inline-flex;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: var(--pya-space-2);
|
|
281
|
+
padding: var(--pya-space-2) var(--pya-space-3);
|
|
282
|
+
border-radius: var(--pya-radius-pill);
|
|
283
|
+
font-weight: 600;
|
|
284
|
+
text-decoration: none;
|
|
285
|
+
}
|
|
286
|
+
.pya-banner__cta[aria-disabled="true"] {
|
|
287
|
+
background: var(--pya-text-muted);
|
|
288
|
+
color: var(--pya-surface);
|
|
289
|
+
cursor: not-allowed;
|
|
290
|
+
opacity: 0.7;
|
|
291
|
+
}
|
|
292
|
+
.pya-banner--warn {
|
|
293
|
+
background: #fef3c7;
|
|
294
|
+
color: #5b3a0a;
|
|
295
|
+
border-left-color: #d97706;
|
|
296
|
+
}
|
|
297
|
+
.pya-banner--warn .pya-banner__cta {
|
|
298
|
+
background: #5b3a0a;
|
|
299
|
+
color: #fff;
|
|
300
|
+
}
|
|
301
|
+
:root[data-theme="oscuro"] .pya-banner--warn {
|
|
302
|
+
background: #3d2c0a;
|
|
303
|
+
color: #fde4a0;
|
|
304
|
+
border-left-color: #f59e0b;
|
|
305
|
+
}
|
|
306
|
+
:root[data-theme="oscuro"] .pya-banner--warn .pya-banner__cta {
|
|
307
|
+
background: #f59e0b;
|
|
308
|
+
color: #1a1207;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.pya-banner--info {
|
|
312
|
+
background: color-mix(in srgb, var(--pya-acc) 6%, transparent);
|
|
313
|
+
color: var(--pya-text);
|
|
314
|
+
border-left-color: var(--pya-acc);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.pya-banner--error {
|
|
318
|
+
background: #fee2e2;
|
|
319
|
+
color: #7f1d1d;
|
|
320
|
+
border-left-color: #b91c1c;
|
|
321
|
+
}
|
|
322
|
+
:root[data-theme="oscuro"] .pya-banner--error {
|
|
323
|
+
background: #3d0a0a;
|
|
324
|
+
color: #fecaca;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* Soft note (order instructions, side hints). */
|
|
328
|
+
.pya-note {
|
|
329
|
+
padding: var(--pya-space-2);
|
|
330
|
+
background: var(--pya-surface-2);
|
|
331
|
+
border-radius: var(--pya-radius-sm);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* Blockquote-style response box (reviews / comments). */
|
|
335
|
+
.pya-quote {
|
|
336
|
+
padding: var(--pya-space-3);
|
|
337
|
+
border-left: 3px solid var(--pya-acc);
|
|
338
|
+
background: var(--pya-surface-2);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
342
|
+
PILLS — bilingual status / tag chips. */
|
|
343
|
+
.pya-pill {
|
|
344
|
+
display: inline-block;
|
|
345
|
+
padding: 2px var(--pya-space-2);
|
|
346
|
+
border-radius: var(--pya-radius-pill);
|
|
347
|
+
background: var(--pya-surface-2);
|
|
348
|
+
color: var(--pya-text);
|
|
349
|
+
font-size: var(--pya-font-size-sm);
|
|
350
|
+
font-weight: 600;
|
|
351
|
+
line-height: 1.4;
|
|
352
|
+
}
|
|
353
|
+
.pya-pill--ai {
|
|
354
|
+
background: #fef3c7;
|
|
355
|
+
color: #92400e;
|
|
356
|
+
}
|
|
357
|
+
.pya-pill--human {
|
|
358
|
+
background: #dcfce7;
|
|
359
|
+
color: #166534;
|
|
360
|
+
}
|
|
361
|
+
:root[data-theme="oscuro"] .pya-pill--ai {
|
|
362
|
+
background: #3d2c0a;
|
|
363
|
+
color: #fde4a0;
|
|
364
|
+
}
|
|
365
|
+
:root[data-theme="oscuro"] .pya-pill--human {
|
|
366
|
+
background: #0a3d22;
|
|
367
|
+
color: #bbf7d0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.pya-pill--locale {
|
|
371
|
+
margin-right: var(--pya-space-2);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.pya-status-chip {
|
|
375
|
+
display: inline-block;
|
|
376
|
+
padding: var(--pya-space-1) var(--pya-space-3);
|
|
377
|
+
border-radius: var(--pya-radius-pill);
|
|
378
|
+
font-size: var(--pya-font-size-sm);
|
|
379
|
+
font-weight: 600;
|
|
380
|
+
}
|
|
381
|
+
.pya-status-chip--active {
|
|
382
|
+
background: #dcfce7;
|
|
383
|
+
color: #166534;
|
|
384
|
+
}
|
|
385
|
+
.pya-status-chip--paused {
|
|
386
|
+
background: #fee2e2;
|
|
387
|
+
color: #991b1b;
|
|
388
|
+
}
|
|
389
|
+
:root[data-theme="oscuro"] .pya-status-chip--active {
|
|
390
|
+
background: #0a3d22;
|
|
391
|
+
color: #bbf7d0;
|
|
392
|
+
}
|
|
393
|
+
:root[data-theme="oscuro"] .pya-status-chip--paused {
|
|
394
|
+
background: #3d0a0a;
|
|
395
|
+
color: #fecaca;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.pya-rating {
|
|
399
|
+
color: #f59e0b;
|
|
400
|
+
font-size: var(--pya-font-size-lg);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
404
|
+
TABLES — admin simple data tables. */
|
|
405
|
+
.pya-table {
|
|
406
|
+
width: 100%;
|
|
407
|
+
border-collapse: collapse;
|
|
408
|
+
}
|
|
409
|
+
.pya-table thead tr {
|
|
410
|
+
text-align: left;
|
|
411
|
+
border-bottom: 1px solid var(--pya-border);
|
|
412
|
+
}
|
|
413
|
+
.pya-table tbody tr {
|
|
414
|
+
border-bottom: 1px solid var(--pya-border);
|
|
415
|
+
}
|
|
416
|
+
.pya-table th,
|
|
417
|
+
.pya-table td {
|
|
418
|
+
padding: var(--pya-space-2);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
422
|
+
SUMMARY (details>summary). */
|
|
423
|
+
.pya-summary {
|
|
424
|
+
cursor: pointer;
|
|
425
|
+
font-weight: 600;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
429
|
+
SKELETONS — shimmer placeholders that mirror real content geometry. */
|
|
430
|
+
.pya-skel {
|
|
431
|
+
background: linear-gradient(
|
|
432
|
+
90deg,
|
|
433
|
+
var(--pya-surface-2) 0%,
|
|
434
|
+
color-mix(in srgb, var(--pya-surface-2) 55%, var(--pya-surface) 45%) 50%,
|
|
435
|
+
var(--pya-surface-2) 100%
|
|
436
|
+
);
|
|
437
|
+
background-size: 200% 100%;
|
|
438
|
+
border-radius: var(--pya-radius-sm);
|
|
439
|
+
color: transparent;
|
|
440
|
+
user-select: none;
|
|
441
|
+
}
|
|
442
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
443
|
+
.pya-skel {
|
|
444
|
+
animation: pya-skel-shimmer 1.4s linear infinite;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
@keyframes pya-skel-shimmer {
|
|
448
|
+
from {
|
|
449
|
+
background-position: 200% 0;
|
|
450
|
+
}
|
|
451
|
+
to {
|
|
452
|
+
background-position: -200% 0;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
.pya-skel-line {
|
|
456
|
+
display: block;
|
|
457
|
+
height: 1em;
|
|
458
|
+
min-width: 4ch;
|
|
459
|
+
border-radius: 4px;
|
|
460
|
+
}
|
|
461
|
+
.pya-skel-line--lg {
|
|
462
|
+
height: 1.25em;
|
|
463
|
+
}
|
|
464
|
+
.pya-skel-line--sm {
|
|
465
|
+
height: 0.85em;
|
|
466
|
+
opacity: 0.7;
|
|
467
|
+
}
|
|
468
|
+
.pya-skel-pill {
|
|
469
|
+
display: inline-block;
|
|
470
|
+
height: 28px;
|
|
471
|
+
min-width: 64px;
|
|
472
|
+
border-radius: var(--pya-radius-pill);
|
|
473
|
+
}
|
|
474
|
+
.pya-skel-circle {
|
|
475
|
+
display: inline-block;
|
|
476
|
+
width: 40px;
|
|
477
|
+
height: 40px;
|
|
478
|
+
border-radius: 50%;
|
|
479
|
+
}
|
|
480
|
+
.pya-skel-thumb {
|
|
481
|
+
width: 64px;
|
|
482
|
+
height: 64px;
|
|
483
|
+
border-radius: var(--pya-radius-sm);
|
|
484
|
+
flex: none;
|
|
485
|
+
}
|
|
486
|
+
.pya-skel--text {
|
|
487
|
+
display: inline-block;
|
|
488
|
+
height: 1em;
|
|
489
|
+
min-width: 8ch;
|
|
490
|
+
vertical-align: middle;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/* Skeleton card silhouette matching real list-card geometry. */
|
|
494
|
+
.pya-skel-card {
|
|
495
|
+
display: flex;
|
|
496
|
+
gap: var(--pya-space-3);
|
|
497
|
+
align-items: center;
|
|
498
|
+
padding: var(--pya-space-4);
|
|
499
|
+
border: 1px solid var(--pya-border);
|
|
500
|
+
border-radius: var(--pya-radius-md);
|
|
501
|
+
min-height: 76px;
|
|
502
|
+
box-sizing: border-box;
|
|
503
|
+
}
|
|
504
|
+
.pya-skel-card__body {
|
|
505
|
+
flex: 1;
|
|
506
|
+
display: flex;
|
|
507
|
+
flex-direction: column;
|
|
508
|
+
gap: 8px;
|
|
509
|
+
min-width: 0;
|
|
510
|
+
}
|
|
511
|
+
.pya-skel-card__row {
|
|
512
|
+
display: flex;
|
|
513
|
+
gap: var(--pya-space-3);
|
|
514
|
+
align-items: center;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* Skeleton silhouette that pixel-matches `<pya-store-card>` geometry.
|
|
518
|
+
156 px photo strip + 12/16/16 body padding + name row + meta line +
|
|
519
|
+
two pills, all sized from the same tokens as the real card so the
|
|
520
|
+
swap is invisible. Used by the customer-site home & stores pages. */
|
|
521
|
+
.pya-store-skel {
|
|
522
|
+
display: flex;
|
|
523
|
+
flex-direction: column;
|
|
524
|
+
overflow: hidden;
|
|
525
|
+
background: var(--pya-surface);
|
|
526
|
+
border: 1px solid var(--pya-border);
|
|
527
|
+
border-radius: var(--pya-radius-md);
|
|
528
|
+
box-shadow: var(--pya-shadow-sm);
|
|
529
|
+
height: 100%;
|
|
530
|
+
/* Matches .pya-card min-height (310 = worst-case real card with both pills). */
|
|
531
|
+
min-height: 310px;
|
|
532
|
+
box-sizing: border-box;
|
|
533
|
+
}
|
|
534
|
+
.pya-store-skel__photo {
|
|
535
|
+
height: 156px;
|
|
536
|
+
border-radius: 0;
|
|
537
|
+
flex: none;
|
|
538
|
+
}
|
|
539
|
+
.pya-store-skel__body {
|
|
540
|
+
padding: var(--pya-space-3) var(--pya-space-4) var(--pya-space-4);
|
|
541
|
+
display: flex;
|
|
542
|
+
flex-direction: column;
|
|
543
|
+
gap: var(--pya-space-2);
|
|
544
|
+
}
|
|
545
|
+
.pya-store-skel__row {
|
|
546
|
+
display: flex;
|
|
547
|
+
justify-content: space-between;
|
|
548
|
+
align-items: center;
|
|
549
|
+
gap: var(--pya-space-2);
|
|
550
|
+
}
|
|
551
|
+
/* Name strip: h3 at font-size-lg (18 px) × line-height ~1.2 ≈ 22 px. */
|
|
552
|
+
.pya-store-skel__name {
|
|
553
|
+
display: inline-block;
|
|
554
|
+
width: 60%;
|
|
555
|
+
height: 22px;
|
|
556
|
+
border-radius: 4px;
|
|
557
|
+
}
|
|
558
|
+
/* Rating pill: 2/9 padding + font-size-sm ≈ 24 px. */
|
|
559
|
+
.pya-store-skel__rating {
|
|
560
|
+
display: inline-block;
|
|
561
|
+
width: 52px;
|
|
562
|
+
height: 24px;
|
|
563
|
+
border-radius: var(--pya-radius-pill);
|
|
564
|
+
}
|
|
565
|
+
/* Meta line: font-size-sm (14 px) × 1.5 ≈ 21 px; top margin space-2. */
|
|
566
|
+
.pya-store-skel__meta {
|
|
567
|
+
display: inline-block;
|
|
568
|
+
width: 78%;
|
|
569
|
+
height: 21px;
|
|
570
|
+
border-radius: 4px;
|
|
571
|
+
margin-top: var(--pya-space-2);
|
|
572
|
+
}
|
|
573
|
+
.pya-store-skel__tags {
|
|
574
|
+
display: flex;
|
|
575
|
+
gap: 7px;
|
|
576
|
+
margin-top: var(--pya-space-3);
|
|
577
|
+
}
|
|
578
|
+
/* Minitag pill: font-size-xs (12 px) + 4/10 padding ≈ 24 px × ~84 px. */
|
|
579
|
+
.pya-store-skel__pill {
|
|
580
|
+
display: inline-block;
|
|
581
|
+
height: 24px;
|
|
582
|
+
width: 84px;
|
|
583
|
+
border-radius: var(--pya-radius-pill);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
587
|
+
PASSKEY login-state card (shared between site + admin login pages). */
|
|
588
|
+
.pya-passkey-status {
|
|
589
|
+
max-width: 420px;
|
|
590
|
+
margin-top: var(--pya-space-5);
|
|
591
|
+
padding: var(--pya-space-5);
|
|
592
|
+
text-align: center;
|
|
593
|
+
border: 1px solid var(--pya-border);
|
|
594
|
+
border-radius: var(--pya-radius-md);
|
|
595
|
+
}
|
|
596
|
+
.pya-passkey-status__icon {
|
|
597
|
+
font-size: 36px;
|
|
598
|
+
margin-bottom: var(--pya-space-3);
|
|
599
|
+
}
|
|
600
|
+
.pya-passkey-status__msg {
|
|
601
|
+
margin: 0 0 var(--pya-space-4);
|
|
602
|
+
color: var(--pya-text);
|
|
603
|
+
font-weight: 600;
|
|
604
|
+
}
|
|
605
|
+
.pya-passkey-status__actions {
|
|
606
|
+
display: flex;
|
|
607
|
+
gap: var(--pya-space-3);
|
|
608
|
+
flex-wrap: wrap;
|
|
609
|
+
justify-content: center;
|
|
610
|
+
}
|
|
611
|
+
.pya-spinner {
|
|
612
|
+
width: 32px;
|
|
613
|
+
height: 32px;
|
|
614
|
+
border: 3px solid var(--pya-border);
|
|
615
|
+
border-top-color: var(--pya-acc);
|
|
616
|
+
border-radius: 50%;
|
|
617
|
+
margin: 0 auto var(--pya-space-3);
|
|
618
|
+
animation: pya-spin 1s linear infinite;
|
|
619
|
+
}
|
|
620
|
+
@keyframes pya-spin {
|
|
621
|
+
to {
|
|
622
|
+
transform: rotate(360deg);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
627
|
+
PROSE — rendered markdown article body. */
|
|
628
|
+
.pya-prose h1 {
|
|
629
|
+
margin: var(--pya-space-3) 0 var(--pya-space-4);
|
|
630
|
+
}
|
|
631
|
+
.pya-prose h2 {
|
|
632
|
+
margin-top: var(--pya-space-5);
|
|
633
|
+
}
|
|
634
|
+
.pya-prose p {
|
|
635
|
+
margin: var(--pya-space-3) 0;
|
|
636
|
+
line-height: 1.6;
|
|
637
|
+
}
|
|
638
|
+
.pya-prose a {
|
|
639
|
+
color: var(--pya-acc);
|
|
640
|
+
text-decoration: underline;
|
|
641
|
+
}
|
|
642
|
+
.pya-prose ul,
|
|
643
|
+
.pya-prose ol {
|
|
644
|
+
padding-left: var(--pya-space-5);
|
|
645
|
+
margin: var(--pya-space-3) 0;
|
|
646
|
+
}
|
|
647
|
+
.pya-prose code {
|
|
648
|
+
background: var(--pya-surface-2);
|
|
649
|
+
padding: 2px 6px;
|
|
650
|
+
border-radius: 4px;
|
|
651
|
+
font-size: 0.92em;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
655
|
+
TILE GRID — large dashboard navigation cards. */
|
|
656
|
+
.pya-tile-grid {
|
|
657
|
+
display: grid;
|
|
658
|
+
gap: var(--pya-space-4);
|
|
659
|
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
660
|
+
}
|
|
661
|
+
.pya-tile {
|
|
662
|
+
display: inline-flex;
|
|
663
|
+
align-items: center;
|
|
664
|
+
gap: var(--pya-space-3);
|
|
665
|
+
justify-content: flex-start;
|
|
666
|
+
padding: var(--pya-space-4);
|
|
667
|
+
border: 1px solid var(--pya-border);
|
|
668
|
+
border-radius: var(--pya-radius-md);
|
|
669
|
+
background: var(--pya-surface);
|
|
670
|
+
color: inherit;
|
|
671
|
+
font-weight: 600;
|
|
672
|
+
text-decoration: none;
|
|
673
|
+
min-height: var(--pya-target-comfortable);
|
|
674
|
+
}
|
|
675
|
+
.pya-tile:hover {
|
|
676
|
+
border-color: var(--pya-acc);
|
|
677
|
+
background: var(--pya-surface-2);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/* ─────────────────────────────────────────────────────────────────────────
|
|
681
|
+
FEEDBACK CARD — shared by reviews + comments. */
|
|
682
|
+
.pya-feedback-card {
|
|
683
|
+
padding: var(--pya-space-4);
|
|
684
|
+
border: 1px solid var(--pya-border);
|
|
685
|
+
border-radius: var(--pya-radius-md);
|
|
686
|
+
background: var(--pya-surface);
|
|
687
|
+
}
|
|
688
|
+
.pya-feedback-card__header {
|
|
689
|
+
display: flex;
|
|
690
|
+
align-items: baseline;
|
|
691
|
+
gap: var(--pya-space-3);
|
|
692
|
+
flex-wrap: wrap;
|
|
693
|
+
}
|
|
694
|
+
.pya-feedback-card__date {
|
|
695
|
+
margin-left: auto;
|
|
696
|
+
color: var(--pya-text-muted);
|
|
697
|
+
font-size: var(--pya-font-size-sm);
|
|
698
|
+
}
|
|
699
|
+
.pya-feedback-card__body {
|
|
700
|
+
margin: var(--pya-space-3) 0 0;
|
|
701
|
+
}
|
|
702
|
+
.pya-feedback-card__response {
|
|
703
|
+
margin-top: var(--pya-space-3);
|
|
704
|
+
}
|
|
705
|
+
.pya-feedback-card__response p {
|
|
706
|
+
margin: var(--pya-space-2) 0 0;
|
|
707
|
+
}
|
|
708
|
+
.pya-feedback-reply {
|
|
709
|
+
margin-top: var(--pya-space-3);
|
|
710
|
+
display: flex;
|
|
711
|
+
gap: var(--pya-space-3);
|
|
712
|
+
align-items: flex-start;
|
|
713
|
+
}
|
|
714
|
+
.pya-feedback-reply .pya-textarea {
|
|
715
|
+
flex: 1;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/* Empty state for feedback lists. */
|
|
719
|
+
.pya-feedback-empty {
|
|
720
|
+
min-height: 120px;
|
|
721
|
+
display: flex;
|
|
722
|
+
align-items: center;
|
|
723
|
+
justify-content: center;
|
|
724
|
+
padding: var(--pya-space-4);
|
|
725
|
+
border: 1px solid var(--pya-border);
|
|
726
|
+
border-radius: var(--pya-radius-md);
|
|
727
|
+
color: var(--pya-text-muted);
|
|
728
|
+
text-align: center;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* Skeleton variant of a feedback card (5-line silhouette). */
|
|
732
|
+
.pya-skel-feedback-card {
|
|
733
|
+
flex-direction: column;
|
|
734
|
+
align-items: stretch;
|
|
735
|
+
gap: var(--pya-space-2);
|
|
736
|
+
min-height: 120px;
|
|
737
|
+
padding: var(--pya-space-4);
|
|
738
|
+
}
|
package/src/glass.css
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* Glass surfaces — backdrop-filter with prefers-contrast: more fallback to
|
|
2
|
+
solid system colors (restores AAA contrast for users who request it). */
|
|
3
|
+
:root {
|
|
4
|
+
--pya-glass-blur: blur(20px) saturate(1.4);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@supports not (backdrop-filter: blur(1px)) {
|
|
8
|
+
:root {
|
|
9
|
+
--pya-glass-blur: none;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@media (prefers-contrast: more) {
|
|
14
|
+
:root {
|
|
15
|
+
--pya-glass-bg: Canvas;
|
|
16
|
+
--pya-glass-tint: Canvas;
|
|
17
|
+
--pya-glass-blur: none;
|
|
18
|
+
--pya-glass-border: ButtonText;
|
|
19
|
+
--pya-text: CanvasText;
|
|
20
|
+
--pya-text-muted: CanvasText;
|
|
21
|
+
--pya-acc: Highlight;
|
|
22
|
+
--pya-acc2: Highlight;
|
|
23
|
+
--pya-text-on-acc: HighlightText;
|
|
24
|
+
--pya-focus-ring-color: Highlight;
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* PyaEats design tokens — single source of truth.
|
|
2
|
+
Consumed by Astro (apps/site, apps/admin) and Lit (packages/ui). */
|
|
3
|
+
@import "./base.css";
|
|
4
|
+
@import "./typography.css";
|
|
5
|
+
@import "./motion.css";
|
|
6
|
+
|
|
7
|
+
/* Palettes — applied via <html data-palette="…">. Mango is the default. */
|
|
8
|
+
@import "./palettes/mango.css";
|
|
9
|
+
@import "./palettes/tomate.css";
|
|
10
|
+
@import "./palettes/lima.css";
|
|
11
|
+
@import "./palettes/miel.css";
|
|
12
|
+
@import "./palettes/caramelo.css";
|
|
13
|
+
|
|
14
|
+
/* Themes — applied via <html data-theme="claro|oscuro">. */
|
|
15
|
+
@import "./themes/claro.css";
|
|
16
|
+
@import "./themes/oscuro.css";
|
|
17
|
+
|
|
18
|
+
/* Glass surfaces — backdrop-filter + prefers-contrast: more fallbacks. */
|
|
19
|
+
@import "./glass.css";
|
|
20
|
+
|
|
21
|
+
/* Utility/component classes — replaces inline-style sprawl across pages. */
|
|
22
|
+
@import "./components.css";
|
package/src/motion.css
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--pya-motion-instant: 0.001ms;
|
|
3
|
+
--pya-motion-fast: 150ms;
|
|
4
|
+
--pya-motion-normal: 250ms;
|
|
5
|
+
--pya-motion-slow: 400ms;
|
|
6
|
+
--pya-motion-page: 300ms;
|
|
7
|
+
|
|
8
|
+
--pya-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
|
9
|
+
--pya-ease-decelerate: cubic-bezier(0, 0, 0.2, 1);
|
|
10
|
+
--pya-ease-accelerate: cubic-bezier(0.4, 0, 1, 1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* AAA SC 2.3.2 / 2.3.3 — motion collapses but stays non-zero so transitionend
|
|
14
|
+
listeners still fire. */
|
|
15
|
+
@media (prefers-reduced-motion: reduce) {
|
|
16
|
+
:root {
|
|
17
|
+
--pya-motion-fast: 0.001ms;
|
|
18
|
+
--pya-motion-normal: 0.001ms;
|
|
19
|
+
--pya-motion-slow: 0.001ms;
|
|
20
|
+
--pya-motion-page: 0.001ms;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* Default palette — mango / durazno / chili.
|
|
2
|
+
--pya-acc and --pya-acc2 are registered <color> via @property so palette
|
|
3
|
+
changes interpolate smoothly. */
|
|
4
|
+
@property --pya-acc {
|
|
5
|
+
syntax: "<color>";
|
|
6
|
+
inherits: true;
|
|
7
|
+
initial-value: #ff8a00;
|
|
8
|
+
}
|
|
9
|
+
@property --pya-acc2 {
|
|
10
|
+
syntax: "<color>";
|
|
11
|
+
inherits: true;
|
|
12
|
+
initial-value: #ff3b1f;
|
|
13
|
+
}
|
|
14
|
+
@property --pya-mesh-1 {
|
|
15
|
+
syntax: "<color>";
|
|
16
|
+
inherits: true;
|
|
17
|
+
initial-value: #ffd166;
|
|
18
|
+
}
|
|
19
|
+
@property --pya-mesh-2 {
|
|
20
|
+
syntax: "<color>";
|
|
21
|
+
inherits: true;
|
|
22
|
+
initial-value: #ff7a3d;
|
|
23
|
+
}
|
|
24
|
+
@property --pya-mesh-3 {
|
|
25
|
+
syntax: "<color>";
|
|
26
|
+
inherits: true;
|
|
27
|
+
initial-value: #ffe39c;
|
|
28
|
+
}
|
|
29
|
+
@property --pya-mesh-4 {
|
|
30
|
+
syntax: "<color>";
|
|
31
|
+
inherits: true;
|
|
32
|
+
initial-value: #ffbf86;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
:root,
|
|
36
|
+
[data-palette="mango"] {
|
|
37
|
+
--pya-acc: #ff8a00;
|
|
38
|
+
--pya-acc2: #ff3b1f;
|
|
39
|
+
--pya-mesh-1: #ffd166;
|
|
40
|
+
--pya-mesh-2: #ff7a3d;
|
|
41
|
+
--pya-mesh-3: #ffe39c;
|
|
42
|
+
--pya-mesh-4: #ffbf86;
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
:root,
|
|
2
|
+
[data-theme="claro"] {
|
|
3
|
+
color-scheme: light;
|
|
4
|
+
|
|
5
|
+
--pya-surface-page: #fff7f0;
|
|
6
|
+
--pya-surface: #ffffff;
|
|
7
|
+
--pya-surface-2: #fff1e8;
|
|
8
|
+
--pya-surface-3: #fbe9dc;
|
|
9
|
+
|
|
10
|
+
--pya-text: #20140a;
|
|
11
|
+
/* Bumped from #836f5f → #7a6555 to pass WCAG AA 4.5:1 on --pya-surface-page (#fff7f0).
|
|
12
|
+
Verified via axe-core (was 4.49:1 → now ≥ 4.7:1). */
|
|
13
|
+
--pya-text-muted: #7a6555;
|
|
14
|
+
--pya-text-soft: #a3917f;
|
|
15
|
+
--pya-text-on-acc: #ffffff;
|
|
16
|
+
|
|
17
|
+
--pya-border: #f1e1d3;
|
|
18
|
+
--pya-border-strong: #e6d2c0;
|
|
19
|
+
|
|
20
|
+
--pya-shadow-sm: 0 2px 10px rgba(120, 60, 20, 0.08);
|
|
21
|
+
--pya-shadow: 0 8px 28px rgba(120, 60, 20, 0.1);
|
|
22
|
+
|
|
23
|
+
--pya-glass-tint: #ffffff;
|
|
24
|
+
--pya-glass-bg: rgba(255, 255, 255, 0.85);
|
|
25
|
+
--pya-glass-border: rgba(255, 255, 255, 0.72);
|
|
26
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[data-theme="oscuro"] {
|
|
2
|
+
color-scheme: dark;
|
|
3
|
+
|
|
4
|
+
--pya-surface-page: #120e0a;
|
|
5
|
+
--pya-surface: #1d1711;
|
|
6
|
+
--pya-surface-2: #261e17;
|
|
7
|
+
--pya-surface-3: #30261d;
|
|
8
|
+
|
|
9
|
+
--pya-text: #fdf6ef;
|
|
10
|
+
--pya-text-muted: #b7a797;
|
|
11
|
+
--pya-text-soft: #8a7a6b;
|
|
12
|
+
--pya-text-on-acc: #ffffff;
|
|
13
|
+
|
|
14
|
+
--pya-border: #322a21;
|
|
15
|
+
--pya-border-strong: #43382c;
|
|
16
|
+
|
|
17
|
+
--pya-shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.35);
|
|
18
|
+
--pya-shadow: 0 10px 34px rgba(0, 0, 0, 0.45);
|
|
19
|
+
|
|
20
|
+
--pya-glass-tint: #14100c;
|
|
21
|
+
--pya-glass-bg: rgba(44, 34, 24, 0.75);
|
|
22
|
+
--pya-glass-border: rgba(255, 255, 255, 0.13);
|
|
23
|
+
|
|
24
|
+
--pya-focus-ring-color: #60a0ff;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (prefers-color-scheme: dark) {
|
|
28
|
+
:root:not([data-theme="claro"]) {
|
|
29
|
+
color-scheme: dark;
|
|
30
|
+
|
|
31
|
+
--pya-surface-page: #120e0a;
|
|
32
|
+
--pya-surface: #1d1711;
|
|
33
|
+
--pya-text: #fdf6ef;
|
|
34
|
+
--pya-text-muted: #b7a797;
|
|
35
|
+
--pya-glass-bg: rgba(44, 34, 24, 0.75);
|
|
36
|
+
--pya-glass-tint: #14100c;
|
|
37
|
+
--pya-focus-ring-color: #60a0ff;
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/ts/tokens.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JS-consumable mirror of the design tokens. Values are CSS `var()` strings so
|
|
3
|
+
* they resolve at use time (theme + palette aware). For static values use the
|
|
4
|
+
* generator in `scripts/generate-ts.ts` to refresh this file from CSS.
|
|
5
|
+
*
|
|
6
|
+
* NEVER use `as const` outside this file — see CODE_STYLE.md.
|
|
7
|
+
*/
|
|
8
|
+
export const PYA_TOKENS = {
|
|
9
|
+
color: {
|
|
10
|
+
text: 'var(--pya-text)',
|
|
11
|
+
textMuted: 'var(--pya-text-muted)',
|
|
12
|
+
surface: 'var(--pya-surface)',
|
|
13
|
+
acc: 'var(--pya-acc)',
|
|
14
|
+
acc2: 'var(--pya-acc2)',
|
|
15
|
+
glassBg: 'var(--pya-glass-bg)',
|
|
16
|
+
glassBorder: 'var(--pya-glass-border)',
|
|
17
|
+
focusRing: 'var(--pya-focus-ring-color)',
|
|
18
|
+
},
|
|
19
|
+
motion: {
|
|
20
|
+
instant: 'var(--pya-motion-instant)',
|
|
21
|
+
fast: 'var(--pya-motion-fast)',
|
|
22
|
+
normal: 'var(--pya-motion-normal)',
|
|
23
|
+
slow: 'var(--pya-motion-slow)',
|
|
24
|
+
page: 'var(--pya-motion-page)',
|
|
25
|
+
easeStandard: 'var(--pya-ease-standard)',
|
|
26
|
+
easeDecelerate: 'var(--pya-ease-decelerate)',
|
|
27
|
+
easeAccelerate: 'var(--pya-ease-accelerate)',
|
|
28
|
+
},
|
|
29
|
+
shape: {
|
|
30
|
+
radiusSm: 'var(--pya-radius-sm)',
|
|
31
|
+
radiusMd: 'var(--pya-radius-md)',
|
|
32
|
+
radiusLg: 'var(--pya-radius-lg)',
|
|
33
|
+
radiusPill: 'var(--pya-radius-pill)',
|
|
34
|
+
},
|
|
35
|
+
space: {
|
|
36
|
+
s1: 'var(--pya-space-1)',
|
|
37
|
+
s2: 'var(--pya-space-2)',
|
|
38
|
+
s3: 'var(--pya-space-3)',
|
|
39
|
+
s4: 'var(--pya-space-4)',
|
|
40
|
+
s5: 'var(--pya-space-5)',
|
|
41
|
+
s6: 'var(--pya-space-6)',
|
|
42
|
+
s7: 'var(--pya-space-7)',
|
|
43
|
+
s8: 'var(--pya-space-8)',
|
|
44
|
+
},
|
|
45
|
+
target: {
|
|
46
|
+
min: 'var(--pya-target-min)',
|
|
47
|
+
comfortable: 'var(--pya-target-comfortable)',
|
|
48
|
+
},
|
|
49
|
+
} as const
|
|
50
|
+
|
|
51
|
+
export type PyaToken = typeof PYA_TOKENS
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--pya-font-primary: "Inter", "Inter Fallback", system-ui, -apple-system, sans-serif;
|
|
3
|
+
--pya-font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;
|
|
4
|
+
|
|
5
|
+
--pya-font-size-xs: 12px;
|
|
6
|
+
--pya-font-size-sm: 14px;
|
|
7
|
+
--pya-font-size-md: 16px;
|
|
8
|
+
--pya-font-size-lg: 18px;
|
|
9
|
+
--pya-font-size-xl: 22px;
|
|
10
|
+
--pya-font-size-2xl: 28px;
|
|
11
|
+
--pya-font-size-3xl: 36px;
|
|
12
|
+
|
|
13
|
+
--pya-line-height-tight: 1.2;
|
|
14
|
+
--pya-line-height-normal: 1.5;
|
|
15
|
+
--pya-line-height-relaxed: 1.7;
|
|
16
|
+
|
|
17
|
+
--pya-tracking-tight: -0.02em;
|
|
18
|
+
--pya-tracking-normal: 0;
|
|
19
|
+
--pya-tracking-wide: 0.04em;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* System fallback metric-matched to Inter — minimises CLS during font swap. */
|
|
23
|
+
@font-face {
|
|
24
|
+
font-family: "Inter Fallback";
|
|
25
|
+
src: local("Arial");
|
|
26
|
+
ascent-override: 90.2%;
|
|
27
|
+
descent-override: 22.48%;
|
|
28
|
+
line-gap-override: 0%;
|
|
29
|
+
size-adjust: 107.4%;
|
|
30
|
+
}
|