@q1travel/design-system 0.27.0 → 0.27.1
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 +1 -1
- package/bin/q1-design.mjs +12 -1
- package/contracts/design.dark.md +9 -1
- package/contracts/design.md +9 -1
- package/contracts/implementation-map.md +6 -6
- package/contracts/interface-guidelines.md +2 -2
- package/contracts/overlay-shadow-migration.md +1 -1
- package/contracts/token-naming.md +15 -3
- package/dist-pkg/design-system.js +3 -3
- package/dist-pkg/style.css +123 -34
- package/dist-pkg/tokens.css +48 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ npm run dev
|
|
|
98
98
|
|
|
99
99
|
**技术栈**:React 19 · TypeScript · Vite 7(文档站 / 库双构建管线)· lucide-react · npm
|
|
100
100
|
|
|
101
|
-
**Token
|
|
101
|
+
**Token 刻度原则**:目标是刻度纯粹、正交,而不是冻结。字号、行高、控件高度、圆角等刻度以既有原生档位序列为基准,序列中定义而本仓缺失的档位可以补齐。业务页面参考其他产品时,只借鉴页面布局、信息架构与间距节奏,并映射到现有档位;仅为贴合某个产品像素而插入孤立私有尺寸的请求,设计系统直接拒绝,无需上报。确有通用诉求、且契合现有比例与刻度演进的新档位,仍走正常的规范评审。完整规则见 `contracts/token-naming.md` 的 Scale Governance。
|
|
102
102
|
|
|
103
103
|
### Sources
|
|
104
104
|
|
package/bin/q1-design.mjs
CHANGED
|
@@ -1060,8 +1060,19 @@ function printUsage() {
|
|
|
1060
1060
|
|
|
1061
1061
|
// 直跑守卫(与 scripts/ci-path-filter.mjs 同款):被 import 时只暴露纯函数,不执行 CLI,
|
|
1062
1062
|
// 这样 Q1-DS-501 的匹配逻辑可以被单测直接覆盖,而不必靠拉起子进程去反推。
|
|
1063
|
+
// 经 npm/pnpm 的 bin 软链启动时 argv[1] 是软链路径,而 import.meta.url 是解析后的真实路径,
|
|
1064
|
+
// 两侧都按 realpath 归一后再比较;pathToFileURL 负责 Windows 盘符与分隔符的 URL 形式。
|
|
1065
|
+
function resolveEntryUrl(entryPath) {
|
|
1066
|
+
try {
|
|
1067
|
+
return pathToFileURL(realpathSync(entryPath)).href;
|
|
1068
|
+
} catch {
|
|
1069
|
+
return pathToFileURL(entryPath).href;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1063
1073
|
const isDirectRun =
|
|
1064
|
-
process.argv[1] &&
|
|
1074
|
+
Boolean(process.argv[1]) &&
|
|
1075
|
+
resolveEntryUrl(fileURLToPath(import.meta.url)) === resolveEntryUrl(process.argv[1]);
|
|
1065
1076
|
|
|
1066
1077
|
if (isDirectRun) {
|
|
1067
1078
|
const subcommand = process.argv[2];
|
package/contracts/design.dark.md
CHANGED
|
@@ -139,7 +139,7 @@ semantic-colors:
|
|
|
139
139
|
text-on-media: "#ffffff"
|
|
140
140
|
border: "{colors.gray-400}"
|
|
141
141
|
border-control: "{colors.gray-600}"
|
|
142
|
-
border-strong: "{colors.gray-
|
|
142
|
+
border-strong: "{colors.gray-500}"
|
|
143
143
|
border-active: "{colors.primary-900}"
|
|
144
144
|
border-subtle: "#2a2a2a"
|
|
145
145
|
text-primary: "{colors.text-primary}"
|
|
@@ -176,15 +176,23 @@ semantic-colors:
|
|
|
176
176
|
auth-border-strong: "#343943"
|
|
177
177
|
auth-surface-card: "#17191d"
|
|
178
178
|
auth-surface-subtle: "#101216"
|
|
179
|
+
auth-surface-input: "#101216"
|
|
180
|
+
auth-surface-hover: "#202329"
|
|
179
181
|
auth-surface-disabled: "#202329"
|
|
180
182
|
auth-surface-media: "#04050a"
|
|
183
|
+
auth-action-hover: "#4434d4"
|
|
184
|
+
auth-action-press: "#2e2b8c"
|
|
181
185
|
auth-action-disabled-bg: "#3b404a"
|
|
182
186
|
auth-action-disabled-border: "#3b404a"
|
|
183
187
|
auth-action-disabled-text: "#a6acb8"
|
|
184
188
|
auth-focus-ring: "rgba(126, 108, 255, 0.52)"
|
|
185
189
|
auth-danger-border: "#d92d20"
|
|
186
190
|
auth-danger-surface: "#241719"
|
|
191
|
+
auth-error-text: "#fb7185"
|
|
187
192
|
auth-field-error-text: "#d32f2f"
|
|
193
|
+
auth-notice-border: "#3d3794"
|
|
194
|
+
auth-notice-surface: "#1d1b3a"
|
|
195
|
+
auth-notice-text: "#cbc4ff"
|
|
188
196
|
auth-media-overlay: "rgba(4, 5, 10, 0.46)"
|
|
189
197
|
auth-media-overlay-hover: "rgba(4, 5, 10, 0.66)"
|
|
190
198
|
auth-on-media-border: "rgba(255, 255, 255, 0.22)"
|
package/contracts/design.md
CHANGED
|
@@ -138,7 +138,7 @@ semantic-colors:
|
|
|
138
138
|
surface-media: "#0a0a0a"
|
|
139
139
|
text-on-media: "#ffffff"
|
|
140
140
|
border: "{colors.gray-alpha-200}"
|
|
141
|
-
border-control: "{
|
|
141
|
+
border-control: "{colors.gray-700}"
|
|
142
142
|
border-strong: "{colors.gray-500}"
|
|
143
143
|
border-active: "{colors.primary-700}"
|
|
144
144
|
border-subtle: "rgba(203, 213, 225, 0.82)"
|
|
@@ -176,15 +176,23 @@ semantic-colors:
|
|
|
176
176
|
auth-border-strong: "#d6dde6"
|
|
177
177
|
auth-surface-card: "#ffffff"
|
|
178
178
|
auth-surface-subtle: "#f8fafc"
|
|
179
|
+
auth-surface-input: "#ffffff"
|
|
180
|
+
auth-surface-hover: "#f6f9fc"
|
|
179
181
|
auth-surface-disabled: "#f6f9fc"
|
|
180
182
|
auth-surface-media: "#04050a"
|
|
183
|
+
auth-action-hover: "#4434d4"
|
|
184
|
+
auth-action-press: "#2e2b8c"
|
|
181
185
|
auth-action-disabled-bg: "#c7c9cc"
|
|
182
186
|
auth-action-disabled-border: "#c7c9cc"
|
|
183
187
|
auth-action-disabled-text: "#ffffff"
|
|
184
188
|
auth-focus-ring: "rgba(83, 58, 253, 0.42)"
|
|
185
189
|
auth-danger-border: "#d92d20"
|
|
186
190
|
auth-danger-surface: "#fff8f7"
|
|
191
|
+
auth-error-text: "#be123c"
|
|
187
192
|
auth-field-error-text: "#d32f2f"
|
|
193
|
+
auth-notice-border: "#b9b9f9"
|
|
194
|
+
auth-notice-surface: "#efebff"
|
|
195
|
+
auth-notice-text: "#2e2b8c"
|
|
188
196
|
auth-media-overlay: "rgba(4, 5, 10, 0.46)"
|
|
189
197
|
auth-media-overlay-hover: "rgba(4, 5, 10, 0.66)"
|
|
190
198
|
auth-on-media-border: "rgba(255, 255, 255, 0.22)"
|
|
@@ -86,7 +86,7 @@ background while keeping its border static. Public props and token values are un
|
|
|
86
86
|
|
|
87
87
|
## Tabs selection ownership
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
Accepted 2026-10-06: `Tabs` accepts optional `value?: string`,
|
|
90
90
|
`defaultValue?: string`, and `onValueChange?: (id: string) => void`. Existing
|
|
91
91
|
consumers need no migration: without these props, selection starts at the first
|
|
92
92
|
enabled item and preserves automatic activation, roving tabindex, and availability
|
|
@@ -98,14 +98,14 @@ changes only when the parent updates `value`. Keyboard navigation follows the
|
|
|
98
98
|
focused enabled tab while the parent update is pending, and a later value update
|
|
99
99
|
does not pull focus from the activated button. Consumers own URL synchronization.
|
|
100
100
|
|
|
101
|
-
## Record activity (
|
|
101
|
+
## Record activity (accepted 2026-10-06)
|
|
102
102
|
|
|
103
103
|
Timeline / TimelineItem (ol/li, 8px token dot or Avatar), Marker (quiet actor/time, date separator), Message (default/pending/failed, caller actions) / Mention (inline action-text on selected surface), Composer (Tier A textarea combobox/listbox, IME guard, Enter send / Shift+Enter newline), Attachment (Progress, retry/remove) are public package components. TimelineCollapsedGroup uses a native disclosure button with aria-expanded and Enter/Space; callers group runs of consecutive system events (docs example uses three or more). Marker unread uses centered action text; caller places and scrolls to the boundary. Message acknowledgements and its caller-owned actions slot display receipt state. Composer also accepts `onMentionQuery(query, {signal})` with 200ms debounce, abort/stale-result rejection, loading/empty/error states and keyboard retry; async lookup takes precedence over static members. Optional `placeholder` and `hint` preserve the input label; member instructions appear only with mention support. Han text may directly precede @, while Unicode word/email interiors do not trigger. Selection inserts text and sets the caret synchronously. Composer commands share the mention combobox/listbox keyboard model and IME guard; known slash commands invoke onCommand instead of onSubmit. Attachment paste/drop passes File[] to onAttach; legacy clicks remain argument-free. Callers own the file picker and object URLs and revoke previews on removal or unmount. Existing tokens only; data, permissions, network and notifications remain caller-owned. Composer accepts optional `triggers` ({char,label,options?,onQuery?,emptyText?,errorText?,toToken?}); single-symbol triggers share debounce, abort/stale rejection, statuses, retry, combobox/listbox keyboard and synchronous caret insertion. Explicit @ overrides legacy mentions/onMentionQuery; otherwise they remain an implicit @ trigger. Slash is reserved for unchanged commands and rejected as a trigger with a development warning. Enabled triggers alone appear in the default hint. Han text may directly precede any trigger; word/email interiors do not trigger. Selection inserts toToken(item), defaulting to char + label, and surviving selections produce {char,id,label} references alongside existing text/mentionIds; references is optional in the type for legacy constructed submissions and always present on component submissions. Editing a token invalidates its reference.
|
|
104
104
|
Timeline / TimelineItem (ol/li, 8px token dot or Avatar), Marker (quiet actor/time, date separator), Message (default/pending/failed, caller actions) / Mention (inline action-text on selected surface), Composer (Tier A textarea combobox/listbox, IME guard, Enter send / Shift+Enter newline), Attachment (Progress, retry/remove) are public package components. `/patterns/record-activity` composes order facts, Switch, chronological activity and a pinned Composer. Existing tokens only; data, permissions, network and notifications remain caller-owned.
|
|
105
105
|
|
|
106
106
|
## ToggleGroup selection ownership
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
Accepted 2026-10-07: public `ToggleGroup` uses the shadcn/Radix name (no corresponding industry-common component). Its `items` API exposes `value`, `label`, optional `icon`, `disabled`, and `ariaLabel`; icon-only items require an icon and `ariaLabel`. `type="single"` accepts string `value` / `defaultValue` and `onValueChange`; `allowEmpty` defaults to false. `type="multiple"` accepts string arrays. Controlled values remain caller-owned; uncontrolled single selection falls back to the first enabled item when its value is absent or unknown and empty selection is disallowed. Disabling an existing selected item retains its pressed state and moves only the tabstop. No existing Switch consumer is migrated.
|
|
109
109
|
|
|
110
110
|
Native buttons expose `aria-pressed` within `role="group"`. Roving tabindex and Arrow / Home / End move focus without selecting; Enter / Space activate through native clicks. Disabled items are skipped. Button-aligned sm32 / md40 / lg48 sizes use existing tokens, quiet selected background and primary text without a selected ring, shared focus shadow, and the shared coarse-pointer 44px TouchTarget with reserved neighbor space. The Tier A keyboard contract lives on `/components/toggle-group`; Switch retains radio semantics and Tabs retains panel semantics.
|
|
111
111
|
## Checkbox task completion circle
|
|
@@ -113,7 +113,7 @@ Native buttons expose `aria-pressed` within `role="group"`. Roving tabindex and
|
|
|
113
113
|
- Documentation: `src/pages/CheckboxPage.tsx`, `CheckboxCircleDemo.tsx`, `src/styles/checkbox-circle-specimen.css`; circle completion is restricted to tasks/checklists and row completion treatment stays consumer-owned.
|
|
114
114
|
- Verification: `scripts/check-checkbox-circle-contract.test.mjs`, `scripts/fixtures/checkbox-circle-api-consumer.tsx`, `scripts/lib/checkbox-circle-contrast.mjs`, `scripts/lib/check-checkbox-circle-browser.mjs`. Package gate covers API/default/native markup/readonly semantics/token existence and both-theme contrast; browser gate covers native toggles, focus without preview, readonly, tone fill, reduced motion and coarse hit targets.
|
|
115
115
|
## Chat messages
|
|
116
|
-
Bubble is separate from Message: chat has optional identity and direction, while Message retains its required author, comment attachments and receipt API unchanged. MessageScroller is separate from Scroller: keyed prepend anchors and message following need ownership of the viewport; the general Scroller API remains unchanged. The shadcn chat reference supplies conversation intent only; quiet
|
|
116
|
+
Bubble is separate from Message: chat has optional identity and direction, while Message retains its required author, comment attachments and receipt API unchanged. MessageScroller is separate from Scroller: keyed prepend anchors and message following need ownership of the viewport; the general Scroller API remains unchanged. The shadcn chat reference supplies conversation intent only; quiet chat surfaces map to existing q1travel panel/selected backgrounds, borders, spacing, radii and Button. No reference CSS, motion background or private tokens are shipped.
|
|
117
117
|
The consumer mapping was checked against downstream chat-style implementations (read-only, no downstream edits).
|
|
118
118
|
| Web capability | Package support / caller boundary |
|
|
119
119
|
| --- | --- |
|
|
@@ -130,8 +130,8 @@ Keyboard: native viewport Arrow/PageUp/PageDown/Home/End; Tab reaches the viewpo
|
|
|
130
130
|
|
|
131
131
|
## Hidden field labels
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
Accepted 2026-10-08: additive `labelHidden?: boolean` (default false) keeps a real associated label through `q1-sr-only` while removing its visual row and gap. Input, Textarea, Select, InputOTP and Checkbox/Radio (including circular Checkbox) expose the shell option; bare mode rejects it. SearchInput and Combobox compositions use `FormField labelHidden`, preserving their existing APIs. Calendar/RangeField does not consume FieldShellProps and retains its existing date-trigger behavior. Errors and descriptions remain visible and connected through `aria-describedby`. Input's existing string `hideLabel` remains the password hide-action copy.
|
|
134
134
|
|
|
135
135
|
`FormField` establishes a relative containing block for hidden labels and empty status regions. Package consumer fixtures enforce label ownership, SSR contracts cover names/messages, and `scripts/lib/check-label-hidden-browser.mjs` checks native labels, visible errors, inline Button alignment and a deterministic 390px scrollable max-content row without page overflow.
|
|
136
136
|
## Control density scale
|
|
137
|
-
|
|
137
|
+
Accepted 2026-10-08: explicitly sized single-line Button, ButtonLink, Input, SearchInput, Select, RangeField trigger, Menu text trigger and FilterChip share sm32 / md40 / lg48 on fine pointers. SearchInput defaults to md and retains native numeric size compatibility; FilterChip defaults to sm32. Menu text triggers default to md40 after the rendered fine-pointer audit confirmed the prior omitted-size trigger measured 38–39px; icon triggers retain their prior behavior. Calendar size remains grid-only; fieldSize controls its trigger. Switch outer sm / md / lg heights align to 32 / 40 / 48px, defaulting to md; coarse-pointer 44px rules remain unchanged. FormField and Select omit empty or absent visible labels; callers supply aria-label, aria-labelledby or a native external label. Select reuses the development-only bare-field accessible-name warning.
|
|
@@ -26,7 +26,7 @@ Adding a clause therefore means answering two questions before the wording: who
|
|
|
26
26
|
- [both] MUST keep mobile control targets at least 44px where touch is expected.
|
|
27
27
|
- [both] MUST use `--q1-touch-target-min` (44px) for the shared centered invisible child-span hit region of Button (all sizes, including compact consumer overrides and icon-only), CopyButton, Menu/DotsMenu triggers, Description info/hint buttons, and the independent FilterChip picker/clear buttons only under `(pointer: coarse), (any-pointer: coarse)`. Preserve every existing coarse-pointer visual-size rule. Only axes still below the minimum receive extra hit area; existing 44px targets do not expand outside their visual frame. The region covers the complete border box: canonical Button/CopyButton/Menu/DotsMenu borders are 1px, while Description borders are none. Consumers overriding a control border width must also set its private `--touch-target-border-width` to the actual per-side width (including zero for `border: none`); asymmetric borders need their own hit-region sizing. FilterChip keeps its default sm32 visual height and existing gaps/margins; its picker and clear hit regions must not overlap. Menu item rows use a real `min-height: var(--q1-touch-target-min)` under the same coarse-pointer query so neighboring rows remain independent. Focus rings stay on the visual control; child hit-region events activate that native control without replacing its pseudo-elements or overflow rules.
|
|
28
28
|
- [both] MUST leave enough space for expanded targets: dense neighbors must not overlap each other's visual frames. Consumers must reserve the expansion on each side and avoid clipping ancestors; if this is impossible, regroup controls or use larger visual controls. The shared region cannot infer neighbor gaps or escape control/ancestor clipping; clipped compact controls must use a visual size of at least 44px.
|
|
29
|
-
- [both] SHOULD
|
|
29
|
+
- [both] SHOULD NOT ship dense measured 24–32px controls to coarse pointers without enlargement: Apple HIG's 44pt guidance and WCAG 2.5.5 motivate q1travel's 44×44 CSS-pixel mobile usability target; pt and CSS pixels are different units.
|
|
30
30
|
- [both] NEVER disable browser zoom.
|
|
31
31
|
- [both] MUST avoid dead zones: if a region looks clickable, the clickable element must cover it.
|
|
32
32
|
- [both] MUST show a loading indicator on in-flight buttons while keeping the original label.
|
|
@@ -213,7 +213,7 @@ These copy clauses keep reader address and empty states consistent with a formal
|
|
|
213
213
|
|
|
214
214
|
### Component Naming
|
|
215
215
|
|
|
216
|
-
- [repo] SHOULD use the
|
|
216
|
+
- [repo] SHOULD use the industry-common component name when one exists, preferring shadcn/ui, then Radix. Invent a name only when neither has one. Name what the component is, not how it is built.
|
|
217
217
|
- [repo] MUST switch public names directly to the new export without deprecated aliases or old documentation route redirects; record the removal as a breaking change in CHANGELOG.
|
|
218
218
|
|
|
219
219
|
### Component Documentation Pages
|
|
@@ -110,7 +110,7 @@ Owner-approved exception (2026-10-05): ErrorMessage, field errors, and ErrorCard
|
|
|
110
110
|
|
|
111
111
|
Migration: `--q1-color-bg-panel` remains `#ffffff` in light mode and changes from `#0a0a0a` to `#1a1a1a` in dark mode. The dark canvas, raised surface, and overlay now use `#121212`, `#222222`, and `#2b2b2b`. Downstream overrides that manually raised panel or overlay backgrounds should be reviewed. The 2026-10-05 owner-approved focus change replaces the brand ring and inset input stroke with the neutral 1px / 4px recipe and preserves resting input borders. Consumers with custom focus visuals should update their screenshots and forced-colors fallback. The unused `--q1-shadow-card` name is removed before release.
|
|
112
112
|
|
|
113
|
-
Owner-approved contrast roles (2026-10-07): `--q1-color-border-control` is the resting form-control boundary (light
|
|
113
|
+
Owner-approved contrast roles (2026-10-07): `--q1-color-border-control` is the resting form-control boundary (light uses `gray-700`, about 3.23:1 on white, since the 2026-10-13 owner decision; dark uses `gray-600`); controls with a strong light border retain that value and opt into this role only in dark mode. Decorative borders retain `border` / `border-strong`. Dark `--q1-color-border-strong` moves from `gray-400` to `gray-500` (2026-10-13) so selected and emphasized outlines stay distinguishable from the default `border`. `--q1-color-accent-graphic` is for brand-colored non-text graphics (light `primary-700`, dark `primary-900`), independent of button action colors. Slider dark unfilled tracks use `gray-500`.
|
|
114
114
|
|
|
115
115
|
`--q1-touch-target-min` (`44px`) is the theme-independent semantic minimum for coarse-pointer hit regions, stored as `sizing.touch-target-min` in both theme contracts. It changes the invisible target, not the visual size.
|
|
116
116
|
|
|
@@ -134,16 +134,24 @@ Shipped domain palette (owner-approved 2026-10-13): `auth`, the cool blue-gray p
|
|
|
134
134
|
| `--q1-color-auth-text-muted` | `#64748d` | `#9ca3af` | Supporting notes |
|
|
135
135
|
| `--q1-color-auth-border-strong` | `#d6dde6` | `#343943` | Card and input borders |
|
|
136
136
|
| `--q1-color-auth-surface-card` | `#ffffff` | `#17191d` | Card |
|
|
137
|
-
| `--q1-color-auth-surface-subtle` | `#f8fafc` | `#101216` | Preview area
|
|
137
|
+
| `--q1-color-auth-surface-subtle` | `#f8fafc` | `#101216` | Preview area |
|
|
138
|
+
| `--q1-color-auth-surface-input` | `#ffffff` | `#101216` | Input background |
|
|
139
|
+
| `--q1-color-auth-surface-hover` | `#f6f9fc` | `#202329` | Hover background of secondary controls and list rows |
|
|
138
140
|
| `--q1-color-auth-surface-disabled` | `#f6f9fc` | `#202329` | Disabled input |
|
|
139
141
|
| `--q1-color-auth-surface-media` | `#04050a` | `#04050a` | Page fallback before the media backdrop loads |
|
|
142
|
+
| `--q1-color-auth-action-hover` | `#4434d4` | `#4434d4` | Submit button, hover |
|
|
143
|
+
| `--q1-color-auth-action-press` | `#2e2b8c` | `#2e2b8c` | Submit button, pressed |
|
|
140
144
|
| `--q1-color-auth-action-disabled-bg` | `#c7c9cc` | `#3b404a` | Disabled submit button background |
|
|
141
145
|
| `--q1-color-auth-action-disabled-border` | `#c7c9cc` | `#3b404a` | Disabled submit button border |
|
|
142
146
|
| `--q1-color-auth-action-disabled-text` | `#ffffff` | `#a6acb8` | Disabled submit button label |
|
|
143
147
|
| `--q1-color-auth-focus-ring` | `rgba(83, 58, 253, 0.42)` | `rgba(126, 108, 255, 0.52)` | Single 3px focus ring |
|
|
144
148
|
| `--q1-color-auth-danger-border` | `#d92d20` | `#d92d20` | Failed validation border |
|
|
145
149
|
| `--q1-color-auth-danger-surface` | `#fff8f7` | `#241719` | Failed validation background |
|
|
150
|
+
| `--q1-color-auth-error-text` | `#be123c` | `#fb7185` | Form-level error message above the fields |
|
|
146
151
|
| `--q1-color-auth-field-error-text` | `#d32f2f` | `#d32f2f` | Field error message |
|
|
152
|
+
| `--q1-color-auth-notice-border` | `#b9b9f9` | `#3d3794` | Session notice border |
|
|
153
|
+
| `--q1-color-auth-notice-surface` | `#efebff` | `#1d1b3a` | Session notice background |
|
|
154
|
+
| `--q1-color-auth-notice-text` | `#2e2b8c` | `#cbc4ff` | Session notice text |
|
|
147
155
|
| `--q1-color-auth-media-overlay` | `rgba(4, 5, 10, 0.46)` | `rgba(4, 5, 10, 0.46)` | Trigger background over a media backdrop |
|
|
148
156
|
| `--q1-color-auth-media-overlay-hover` | `rgba(4, 5, 10, 0.66)` | `rgba(4, 5, 10, 0.66)` | Same trigger, hover |
|
|
149
157
|
| `--q1-color-auth-on-media-border` | `rgba(255, 255, 255, 0.22)` | `rgba(255, 255, 255, 0.22)` | Trigger border over a media backdrop |
|
|
@@ -151,6 +159,10 @@ Shipped domain palette (owner-approved 2026-10-13): `auth`, the cool blue-gray p
|
|
|
151
159
|
|
|
152
160
|
The theme contracts register these as `semantic-colors.auth-*`, and `check:token-package` asserts that each contract value resolves to the runtime token in both themes.
|
|
153
161
|
|
|
162
|
+
The input background, the hover background, and the secondary button background are separate roles. Secondary buttons inside the card use `--q1-color-auth-surface-card`, because they sit on the card surface; `--q1-color-auth-surface-input` is reserved for text entry. `--q1-color-auth-surface-hover` is the interaction hover and is not substituted by `--q1-color-auth-surface-disabled`, even where the values match. `--q1-color-auth-action-hover` and `--q1-color-auth-action-press` keep the same value in both themes, unlike the shared `--q1-color-action-primary-hover` / `-press`. `--q1-color-auth-error-text` is the form-level error and stays distinct from `--q1-color-auth-field-error-text`.
|
|
163
|
+
|
|
164
|
+
Fixed-light subtree: an account entry surface that must stay light regardless of the page theme sets `data-q1-theme="light"` on its subtree root. Inside that subtree every `--q1-color-auth-*` token resolves to its light value, while the neutral chain and all other tokens keep following the root theme. The selector covers the auth palette only and is not a general theme switch: inside the subtree, every theme-dependent color comes from the auth palette, and shared tokens are used only where their value is the same in both themes, such as `--q1-color-action-primary`. Apply it to a descendant element, not to the root element that carries `data-theme`. The build and `check:token-package` assert that `[data-q1-theme='light']` redeclares every auth token, and nothing else, with exactly the light values.
|
|
165
|
+
|
|
154
166
|
### Component Tokens
|
|
155
167
|
|
|
156
168
|
Component tokens are allowed only when a shared component needs a stable public customization surface. Do not create component tokens for one page or one specimen.
|
|
@@ -204,7 +216,7 @@ gets an input with no border and no background.
|
|
|
204
216
|
|
|
205
217
|
## Scale Governance
|
|
206
218
|
|
|
207
|
-
- The goal is a pure, orthogonal scale, not a frozen one. The size, typography, radius, and control-height scales are based on
|
|
219
|
+
- The goal is a pure, orthogonal scale, not a frozen one. The size, typography, radius, and control-height scales are based on the established native step sequence (see the step tables in this contract); a missing step the sequence defines may be filled (for example `label-12`, 12px / 16px).
|
|
208
220
|
- Product surfaces may take page layout, information architecture, and spacing rhythm from other products, but MUST map them onto existing steps. A request to add an isolated, non-standard step only so one surface matches another product's pixels is declined by the design system without escalation.
|
|
209
221
|
- A genuine, reusable need that fits the existing ratios and evolves the scale orthogonally still goes through normal design review. A new step must serve many surfaces, not close a gap of a few pixels on one page.
|
|
210
222
|
|
|
@@ -7051,7 +7051,7 @@ function gs({
|
|
|
7051
7051
|
}
|
|
7052
7052
|
);
|
|
7053
7053
|
}
|
|
7054
|
-
const Le = 8, Qn =
|
|
7054
|
+
const Le = 8, Qn = 8, Zn = typeof document > "u" ? ne : pe;
|
|
7055
7055
|
let We = null, Tt = 0, fn = !1;
|
|
7056
7056
|
function _s() {
|
|
7057
7057
|
if (typeof document > "u") return null;
|
|
@@ -10559,7 +10559,7 @@ function xu({ label: e, entries: t, withChevron: r, align: o = "start", triggerC
|
|
|
10559
10559
|
] });
|
|
10560
10560
|
}
|
|
10561
10561
|
function Pu({ options: e, defaultSelected: t = [], placeholder: r, onChange: o }) {
|
|
10562
|
-
const i = ue(), a = `${i}-listbox`, s = `${i}-summary`, [c, u] = j(!1), [f, h] = j(t), [p, m] = j(-1), [_, g] = j(), l = q(null), v = q(null), b = q(null), { portalPopup: w } = He(c, v, b, () => u(!1)), I = q(null), N = q([]), D = q(void 0), M = q(!1);
|
|
10562
|
+
const i = ue(), a = `${i}-listbox`, s = `${i}-summary`, [c, u] = j(!1), [f, h] = j(t), [p, m] = j(-1), [_, g] = j(), l = q(null), v = q(null), b = q(null), { portalPopup: w } = He(c, v, b, () => u(!1), { gapToken: "--q1-space-2" }), I = q(null), N = q([]), D = q(void 0), M = q(!1);
|
|
10563
10563
|
function C(A) {
|
|
10564
10564
|
h(A), o?.(A);
|
|
10565
10565
|
}
|
|
@@ -12057,7 +12057,7 @@ function Fu({ trigger: e, children: t, openDelay: r = 0, closeDelay: o = 90, sid
|
|
|
12057
12057
|
}, []);
|
|
12058
12058
|
const { portalPopup: C, placement: S, onOverlayKeyDown: L } = He(c, h, p, (k) => {
|
|
12059
12059
|
N(), k === "escape" && h.current?.focus({ preventScroll: !0 });
|
|
12060
|
-
}, { width: "content", side: i, align: a, present: w, focusOnOpen: !1, restoreFocus: !1, outsidePointer: !0, outsidePointerEvent: "down" }), P = [e.props["aria-describedby"], c ? f : void 0].filter(Boolean).join(" ") || void 0;
|
|
12060
|
+
}, { width: "content", side: i, align: a, gapToken: "--q1-space-2", present: w, focusOnOpen: !1, restoreFocus: !1, outsidePointer: !0, outsidePointerEvent: "down" }), P = [e.props["aria-describedby"], c ? f : void 0].filter(Boolean).join(" ") || void 0;
|
|
12061
12061
|
return /* @__PURE__ */ y(he, { children: [
|
|
12062
12062
|
/* @__PURE__ */ n(
|
|
12063
12063
|
"span",
|
package/dist-pkg/style.css
CHANGED
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
--q1-color-bg-media: #0a0a0a;
|
|
143
143
|
--q1-color-text-on-media: #ffffff;
|
|
144
144
|
--q1-color-border: var(--q1-color-gray-alpha-200);
|
|
145
|
-
--q1-color-border-control: var(--q1-color-
|
|
145
|
+
--q1-color-border-control: var(--q1-color-gray-700);
|
|
146
146
|
--q1-color-border-subtle: rgba(203, 213, 225, 0.82);
|
|
147
147
|
--q1-color-border-strong: var(--q1-color-gray-500);
|
|
148
148
|
--q1-color-border-active: var(--q1-color-primary-700);
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
--q1-color-status-info-text: #0064b8;
|
|
175
175
|
--q1-color-status-info-surface: var(--q1-color-blue-100);
|
|
176
176
|
--q1-color-status-info-border: #abcff0;
|
|
177
|
-
/* 域色板 · auth
|
|
177
|
+
/* 域色板 · auth:账户入口专用的冷蓝灰色板。只在账户入口域内成组使用,
|
|
178
178
|
不与中性链 token 混用;值独立于中性链,明暗各一套。 */
|
|
179
179
|
--q1-color-auth-text-primary: #061b31;
|
|
180
180
|
--q1-color-auth-text-secondary: #273951;
|
|
@@ -182,15 +182,23 @@
|
|
|
182
182
|
--q1-color-auth-border-strong: #d6dde6;
|
|
183
183
|
--q1-color-auth-surface-card: #ffffff;
|
|
184
184
|
--q1-color-auth-surface-subtle: #f8fafc;
|
|
185
|
+
--q1-color-auth-surface-input: #ffffff;
|
|
186
|
+
--q1-color-auth-surface-hover: #f6f9fc;
|
|
185
187
|
--q1-color-auth-surface-disabled: #f6f9fc;
|
|
186
188
|
--q1-color-auth-surface-media: #04050a;
|
|
189
|
+
--q1-color-auth-action-hover: #4434d4;
|
|
190
|
+
--q1-color-auth-action-press: #2e2b8c;
|
|
187
191
|
--q1-color-auth-action-disabled-bg: #c7c9cc;
|
|
188
192
|
--q1-color-auth-action-disabled-border: #c7c9cc;
|
|
189
193
|
--q1-color-auth-action-disabled-text: #ffffff;
|
|
190
194
|
--q1-color-auth-focus-ring: rgba(83, 58, 253, 0.42);
|
|
191
195
|
--q1-color-auth-danger-border: #d92d20;
|
|
192
196
|
--q1-color-auth-danger-surface: #fff8f7;
|
|
197
|
+
--q1-color-auth-error-text: #be123c;
|
|
193
198
|
--q1-color-auth-field-error-text: #d32f2f;
|
|
199
|
+
--q1-color-auth-notice-border: #b9b9f9;
|
|
200
|
+
--q1-color-auth-notice-surface: #efebff;
|
|
201
|
+
--q1-color-auth-notice-text: #2e2b8c;
|
|
194
202
|
--q1-color-auth-media-overlay: rgba(4, 5, 10, 0.46);
|
|
195
203
|
--q1-color-auth-media-overlay-hover: rgba(4, 5, 10, 0.66);
|
|
196
204
|
--q1-color-auth-on-media-border: rgba(255, 255, 255, 0.22);
|
|
@@ -475,8 +483,8 @@
|
|
|
475
483
|
--q1-color-border: var(--q1-color-gray-400);
|
|
476
484
|
--q1-color-border-control: var(--q1-color-gray-600);
|
|
477
485
|
--q1-color-border-subtle: #2a2a2a;
|
|
478
|
-
/*
|
|
479
|
-
--q1-color-border-strong: var(--q1-color-gray-
|
|
486
|
+
/* 比 border 高一档(gray-500):选中描边在暗色下可辨,仍不泛白。 */
|
|
487
|
+
--q1-color-border-strong: var(--q1-color-gray-500);
|
|
480
488
|
--q1-color-border-active: var(--q1-color-primary-900);
|
|
481
489
|
--q1-color-border-overlay: color-mix(in srgb, #ffffff 14%, transparent);
|
|
482
490
|
--q1-color-text-primary: var(--q1-color-gray-1000);
|
|
@@ -504,7 +512,7 @@
|
|
|
504
512
|
--q1-color-status-info-text: var(--q1-color-blue-900);
|
|
505
513
|
--q1-color-status-info-surface: var(--q1-color-blue-100);
|
|
506
514
|
--q1-color-status-info-border: #194f81;
|
|
507
|
-
/* 域色板 · auth
|
|
515
|
+
/* 域色板 · auth:账户入口专用的冷蓝灰色板。只在账户入口域内成组使用,
|
|
508
516
|
不与中性链 token 混用;值独立于中性链,明暗各一套。 */
|
|
509
517
|
--q1-color-auth-text-primary: #f3f4f6;
|
|
510
518
|
--q1-color-auth-text-secondary: #d1d5db;
|
|
@@ -512,15 +520,23 @@
|
|
|
512
520
|
--q1-color-auth-border-strong: #343943;
|
|
513
521
|
--q1-color-auth-surface-card: #17191d;
|
|
514
522
|
--q1-color-auth-surface-subtle: #101216;
|
|
523
|
+
--q1-color-auth-surface-input: #101216;
|
|
524
|
+
--q1-color-auth-surface-hover: #202329;
|
|
515
525
|
--q1-color-auth-surface-disabled: #202329;
|
|
516
526
|
--q1-color-auth-surface-media: #04050a;
|
|
527
|
+
--q1-color-auth-action-hover: #4434d4;
|
|
528
|
+
--q1-color-auth-action-press: #2e2b8c;
|
|
517
529
|
--q1-color-auth-action-disabled-bg: #3b404a;
|
|
518
530
|
--q1-color-auth-action-disabled-border: #3b404a;
|
|
519
531
|
--q1-color-auth-action-disabled-text: #a6acb8;
|
|
520
532
|
--q1-color-auth-focus-ring: rgba(126, 108, 255, 0.52);
|
|
521
533
|
--q1-color-auth-danger-border: #d92d20;
|
|
522
534
|
--q1-color-auth-danger-surface: #241719;
|
|
535
|
+
--q1-color-auth-error-text: #fb7185;
|
|
523
536
|
--q1-color-auth-field-error-text: #d32f2f;
|
|
537
|
+
--q1-color-auth-notice-border: #3d3794;
|
|
538
|
+
--q1-color-auth-notice-surface: #1d1b3a;
|
|
539
|
+
--q1-color-auth-notice-text: #cbc4ff;
|
|
524
540
|
--q1-color-auth-media-overlay: rgba(4, 5, 10, 0.46);
|
|
525
541
|
--q1-color-auth-media-overlay-hover: rgba(4, 5, 10, 0.66);
|
|
526
542
|
--q1-color-auth-on-media-border: rgba(255, 255, 255, 0.22);
|
|
@@ -533,6 +549,39 @@
|
|
|
533
549
|
--q1-color-action-text: var(--q1-color-primary-900);
|
|
534
550
|
}
|
|
535
551
|
|
|
552
|
+
/* 固定浅色子树:在任意子树根元素上设置 data-q1-theme="light",
|
|
553
|
+
子树内的 auth 域色板固定取浅色值,不随根元素主题切换;只覆盖 auth 组,中性链照常跟随主题。
|
|
554
|
+
取值与 :root 中的 auth 浅色值逐项一致,由构建与 check:token-package 校验。 */
|
|
555
|
+
[data-q1-theme='light'] {
|
|
556
|
+
--q1-color-auth-text-primary: #061b31;
|
|
557
|
+
--q1-color-auth-text-secondary: #273951;
|
|
558
|
+
--q1-color-auth-text-muted: #64748d;
|
|
559
|
+
--q1-color-auth-border-strong: #d6dde6;
|
|
560
|
+
--q1-color-auth-surface-card: #ffffff;
|
|
561
|
+
--q1-color-auth-surface-subtle: #f8fafc;
|
|
562
|
+
--q1-color-auth-surface-input: #ffffff;
|
|
563
|
+
--q1-color-auth-surface-hover: #f6f9fc;
|
|
564
|
+
--q1-color-auth-surface-disabled: #f6f9fc;
|
|
565
|
+
--q1-color-auth-surface-media: #04050a;
|
|
566
|
+
--q1-color-auth-action-hover: #4434d4;
|
|
567
|
+
--q1-color-auth-action-press: #2e2b8c;
|
|
568
|
+
--q1-color-auth-action-disabled-bg: #c7c9cc;
|
|
569
|
+
--q1-color-auth-action-disabled-border: #c7c9cc;
|
|
570
|
+
--q1-color-auth-action-disabled-text: #ffffff;
|
|
571
|
+
--q1-color-auth-focus-ring: rgba(83, 58, 253, 0.42);
|
|
572
|
+
--q1-color-auth-danger-border: #d92d20;
|
|
573
|
+
--q1-color-auth-danger-surface: #fff8f7;
|
|
574
|
+
--q1-color-auth-error-text: #be123c;
|
|
575
|
+
--q1-color-auth-field-error-text: #d32f2f;
|
|
576
|
+
--q1-color-auth-notice-border: #b9b9f9;
|
|
577
|
+
--q1-color-auth-notice-surface: #efebff;
|
|
578
|
+
--q1-color-auth-notice-text: #2e2b8c;
|
|
579
|
+
--q1-color-auth-media-overlay: rgba(4, 5, 10, 0.46);
|
|
580
|
+
--q1-color-auth-media-overlay-hover: rgba(4, 5, 10, 0.66);
|
|
581
|
+
--q1-color-auth-on-media-border: rgba(255, 255, 255, 0.22);
|
|
582
|
+
--q1-color-auth-on-media-border-hover: rgba(255, 255, 255, 0.34);
|
|
583
|
+
}
|
|
584
|
+
|
|
536
585
|
/* 字体继承兜底(:where 零特异度,消费方自己的 body 样式可无条件覆盖)。 */
|
|
537
586
|
:where(body) {
|
|
538
587
|
font-family: var(--q1-font-sans);
|
|
@@ -995,11 +1044,11 @@
|
|
|
995
1044
|
.input-control:hover:not(:focus-within):not(:has(.form-control:disabled)):not(
|
|
996
1045
|
:has(.form-control[aria-invalid='true'])
|
|
997
1046
|
):not(.form-field--error .input-control) {
|
|
998
|
-
border-color: var(--q1-color-
|
|
1047
|
+
border-color: var(--q1-color-gray-800);
|
|
999
1048
|
}
|
|
1000
1049
|
|
|
1001
1050
|
.input-control:has(.form-control:focus) {
|
|
1002
|
-
border-color: var(--q1-input-border-focus, var(--q1-color-border));
|
|
1051
|
+
border-color: var(--q1-input-border-focus, var(--q1-input-border, var(--q1-color-border-control)));
|
|
1003
1052
|
box-shadow: var(--q1-input-focus-ring, var(--q1-shadow-focus-input));
|
|
1004
1053
|
}
|
|
1005
1054
|
|
|
@@ -1078,7 +1127,7 @@
|
|
|
1078
1127
|
min-height: 0;
|
|
1079
1128
|
}
|
|
1080
1129
|
|
|
1081
|
-
/* 外框 overflow
|
|
1130
|
+
/* 外框 overflow 会裁掉按钮向外扩的焦点环。因此外框保留裁切并照常画
|
|
1082
1131
|
聚焦光晕(焦点仍在控件内),按钮自己的环改为向内收的 outline,整圈落在按钮可见区内,
|
|
1083
1132
|
从输入框 Tab 到按钮时焦点位置可辨;outline 在强制颜色模式下同样可见。 */
|
|
1084
1133
|
.input-control:has(.input-control__action .button:focus-visible) {
|
|
@@ -1306,7 +1355,7 @@
|
|
|
1306
1355
|
}
|
|
1307
1356
|
|
|
1308
1357
|
.textarea-control:hover:not(:focus-within):not(:has(.form-control:disabled)) {
|
|
1309
|
-
border-color: var(--q1-color-
|
|
1358
|
+
border-color: var(--q1-color-gray-800);
|
|
1310
1359
|
}
|
|
1311
1360
|
|
|
1312
1361
|
.textarea-control:has(.form-control:focus) {
|
|
@@ -1363,12 +1412,12 @@
|
|
|
1363
1412
|
.form-control--input:hover:not(:disabled):not(:focus):not([aria-invalid='true']):not(
|
|
1364
1413
|
.form-field--error .form-control--input
|
|
1365
1414
|
) {
|
|
1366
|
-
border-color: var(--q1-color-
|
|
1415
|
+
border-color: var(--q1-color-gray-800);
|
|
1367
1416
|
}
|
|
1368
1417
|
|
|
1369
1418
|
/* 输入类指针与键盘焦点一致,边框与半透明光晕紧贴。 */
|
|
1370
1419
|
.form-control:focus {
|
|
1371
|
-
border-color: var(--q1-input-border-focus, var(--q1-color-border));
|
|
1420
|
+
border-color: var(--q1-input-border-focus, var(--q1-input-border, var(--q1-color-border-control)));
|
|
1372
1421
|
box-shadow: var(--q1-input-focus-ring, var(--q1-shadow-focus-input));
|
|
1373
1422
|
}
|
|
1374
1423
|
|
|
@@ -1418,7 +1467,7 @@
|
|
|
1418
1467
|
.control-option {
|
|
1419
1468
|
position: relative;
|
|
1420
1469
|
display: grid;
|
|
1421
|
-
/* 盒 16px
|
|
1470
|
+
/* 盒 16px 统一(与 Radio 家族一致,16×16)。 */
|
|
1422
1471
|
grid-template-columns: 16px minmax(0, 1fr);
|
|
1423
1472
|
align-items: start;
|
|
1424
1473
|
gap: var(--q1-space-3);
|
|
@@ -1546,7 +1595,7 @@
|
|
|
1546
1595
|
}
|
|
1547
1596
|
}
|
|
1548
1597
|
|
|
1549
|
-
/* Checkbox
|
|
1598
|
+
/* Checkbox 选中为前景单色(与 Radio 家族统一):text-primary 填充 + 面板色反色勾,
|
|
1550
1599
|
两主题自适应;「选中」是状态非动作,不占用 indigo 动作色。 */
|
|
1551
1600
|
.control-option input:checked + .control-option__visual {
|
|
1552
1601
|
border-color: var(--q1-color-text-primary);
|
|
@@ -3994,7 +4043,7 @@ a[href]:has(> .book):focus-visible {
|
|
|
3994
4043
|
}
|
|
3995
4044
|
|
|
3996
4045
|
/* 线型(line)变体:无外框、无卡片底色与内边距,只保留 tablist 的 1px 基线,贴在页面标题下方;
|
|
3997
|
-
|
|
4046
|
+
规格:标签上下 14px、文字 14/20 常规字重,选中项 2px 前景色指示线与基线重合,无位移动画。 */
|
|
3998
4047
|
.tabs-root--line {
|
|
3999
4048
|
border: 0;
|
|
4000
4049
|
border-radius: 0;
|
|
@@ -4134,7 +4183,7 @@ a[href]:has(> .book):focus-visible {
|
|
|
4134
4183
|
min-height: 44px;
|
|
4135
4184
|
}
|
|
4136
4185
|
|
|
4137
|
-
/*
|
|
4186
|
+
/* 线型保持标签自然宽度,不拉伸等分,指示线长度随文字;高度已超过 44px 触控目标。 */
|
|
4138
4187
|
.tabs-control--line .tabs-control__tab {
|
|
4139
4188
|
width: auto;
|
|
4140
4189
|
min-height: 0;
|
|
@@ -4438,23 +4487,20 @@ a[href]:has(> .book):focus-visible {
|
|
|
4438
4487
|
font-weight: var(--q1-font-weight-medium);
|
|
4439
4488
|
}
|
|
4440
4489
|
|
|
4441
|
-
|
|
4490
|
+
/* 区间起点与终点同一扁平实底,不加细框;端点状态由 gridcell 的 aria-selected 表达。 */
|
|
4491
|
+
.calendar__day--start,
|
|
4492
|
+
.calendar__day--end {
|
|
4442
4493
|
background: var(--q1-color-selection-bg);
|
|
4443
4494
|
border-color: transparent;
|
|
4444
4495
|
color: var(--q1-color-selection-fg);
|
|
4445
4496
|
font-weight: var(--q1-font-weight-medium);
|
|
4446
4497
|
}
|
|
4447
4498
|
|
|
4448
|
-
.calendar__day--start:hover:not(:disabled)
|
|
4499
|
+
.calendar__day--start:hover:not(:disabled),
|
|
4500
|
+
.calendar__day--end:hover:not(:disabled) {
|
|
4449
4501
|
background: var(--q1-color-selection-bg);
|
|
4450
4502
|
}
|
|
4451
4503
|
|
|
4452
|
-
.calendar__day--end {
|
|
4453
|
-
border-color: var(--q1-color-text-primary);
|
|
4454
|
-
color: var(--q1-color-text-primary);
|
|
4455
|
-
font-weight: var(--q1-font-weight-medium);
|
|
4456
|
-
}
|
|
4457
|
-
|
|
4458
4504
|
.calendar__day--outside {
|
|
4459
4505
|
color: var(--q1-color-text-muted);
|
|
4460
4506
|
opacity: 0.4;
|
|
@@ -6191,6 +6237,10 @@ a.context-card__trigger {
|
|
|
6191
6237
|
font-weight: 600;
|
|
6192
6238
|
line-height: 1.2;
|
|
6193
6239
|
color: var(--q1-color-text-primary);
|
|
6240
|
+
/* 指标值只占一行:过长时省略,完整值交给 title,卡片高度与比例不随窄屏折行变化。 */
|
|
6241
|
+
overflow: hidden;
|
|
6242
|
+
white-space: nowrap;
|
|
6243
|
+
text-overflow: ellipsis;
|
|
6194
6244
|
}
|
|
6195
6245
|
|
|
6196
6246
|
.q1-card__metric-label {
|
|
@@ -10754,8 +10804,6 @@ a.q1-navigation__item[aria-disabled='true'] {
|
|
|
10754
10804
|
}
|
|
10755
10805
|
.q1-json-view__row--highlight {
|
|
10756
10806
|
border-radius: var(--q1-radius-1);
|
|
10757
|
-
background: var(--q1-color-bg-panel);
|
|
10758
|
-
box-shadow: inset 2px 0 0 var(--q1-color-action-primary);
|
|
10759
10807
|
padding-left: var(--q1-space-2);
|
|
10760
10808
|
margin-left: calc(-1 * var(--q1-space-2));
|
|
10761
10809
|
}
|
|
@@ -11534,7 +11582,7 @@ a.q1-navigation__item[aria-disabled='true'] {
|
|
|
11534
11582
|
flex: none;
|
|
11535
11583
|
display: grid;
|
|
11536
11584
|
place-items: center;
|
|
11537
|
-
/* 角标 16px 与 Checkbox/Radio
|
|
11585
|
+
/* 角标 16px 与 Checkbox/Radio 家族统一(选项控件盒统一 16px)。 */
|
|
11538
11586
|
width: 16px;
|
|
11539
11587
|
height: 16px;
|
|
11540
11588
|
border: 1px solid var(--q1-color-border-strong);
|
|
@@ -11554,13 +11602,8 @@ a.q1-navigation__item[aria-disabled='true'] {
|
|
|
11554
11602
|
opacity: 0;
|
|
11555
11603
|
}
|
|
11556
11604
|
|
|
11557
|
-
/*
|
|
11558
|
-
「选中」是状态非动作,不占 indigo
|
|
11559
|
-
.choicebox:has(.choicebox__input:checked) {
|
|
11560
|
-
border-color: var(--q1-color-text-primary);
|
|
11561
|
-
background: color-mix(in srgb, var(--q1-color-text-primary) 5%, var(--q1-color-bg-panel));
|
|
11562
|
-
}
|
|
11563
|
-
|
|
11605
|
+
/* 选中态:卡片描边与浅底走共享选中配方(selection-recipe.css),角标仍用前景色表达选中;
|
|
11606
|
+
「选中」是状态非动作,不占 indigo。标题不变色。 */
|
|
11564
11607
|
.choicebox:has(.choicebox__input:checked) .choicebox__extra {
|
|
11565
11608
|
display: block;
|
|
11566
11609
|
}
|
|
@@ -11593,7 +11636,7 @@ a.q1-navigation__item[aria-disabled='true'] {
|
|
|
11593
11636
|
|
|
11594
11637
|
@media (hover: hover) and (pointer: fine) {
|
|
11595
11638
|
.choicebox:has(.choicebox__input:not(:checked):not(:disabled)):hover {
|
|
11596
|
-
border-color: var(--q1-color-
|
|
11639
|
+
border-color: var(--q1-color-gray-800);
|
|
11597
11640
|
}
|
|
11598
11641
|
}
|
|
11599
11642
|
|
|
@@ -14045,6 +14088,13 @@ a.q1-navigation__item[aria-disabled='true'] {
|
|
|
14045
14088
|
border-color: var(--q1-color-border-control);
|
|
14046
14089
|
}
|
|
14047
14090
|
|
|
14091
|
+
/* 亮色 OTP 格子、时段触发器、Choicebox 未选中卡同样走控件边界角色,不再用 8% 黑分隔线。 */
|
|
14092
|
+
.cal-period__trigger,
|
|
14093
|
+
.q1-verification-code:not(:has(:disabled, [aria-invalid='true'])) .q1-verification-code__slot,
|
|
14094
|
+
.choicebox:not(:has(:checked, :disabled)) {
|
|
14095
|
+
border-color: var(--q1-color-border-control);
|
|
14096
|
+
}
|
|
14097
|
+
|
|
14048
14098
|
/* 控件基础边框统一走 --q1-color-border-control;禁用态保持原来的弱边界 --q1-color-border,
|
|
14049
14099
|
不随控件边界角色加深(暗色下 border-control 比 border 更亮)。 */
|
|
14050
14100
|
.input-control:has(.form-control:disabled):not(:has([aria-invalid='true'])):not(.form-field--error .input-control),
|
|
@@ -14160,3 +14210,42 @@ a.q1-navigation__item[aria-disabled='true'] {
|
|
|
14160
14210
|
:root[data-theme='dark'] .multi-select__check:not(.multi-select__check--on) {
|
|
14161
14211
|
border-color: var(--q1-color-border-control);
|
|
14162
14212
|
}
|
|
14213
|
+
|
|
14214
|
+
/* 选中态配方:选中 = 1px --q1-color-border-strong 描边 + 浅底(亮色 --q1-color-bg-subtle,
|
|
14215
|
+
暗色 --q1-color-bg-raised),不叠焦点光晕。Choicebox 卡片描边走控件边界角色 --q1-color-border-control。
|
|
14216
|
+
有实体边框的表面只换 border-color;无边框的分段项用 inset 1px 描边,不改变尺寸。
|
|
14217
|
+
键盘聚焦时 inset 描边让位给共享焦点环,同一时刻只显示一层。禁用态沿用各组件自己的降级规则。随包样式发布。 */
|
|
14218
|
+
.choicebox:has(.choicebox__input:checked:not(:disabled)),
|
|
14219
|
+
.pagination__button--current:not(:disabled, [aria-disabled='true']) {
|
|
14220
|
+
border-color: var(--q1-color-border-strong);
|
|
14221
|
+
background: var(--q1-color-bg-subtle);
|
|
14222
|
+
}
|
|
14223
|
+
|
|
14224
|
+
.choicebox:has(.choicebox__input:checked:not(:disabled)) {
|
|
14225
|
+
border-color: var(--q1-color-border-control);
|
|
14226
|
+
}
|
|
14227
|
+
|
|
14228
|
+
.toggle-group__item[aria-pressed='true']:not(:focus-visible),
|
|
14229
|
+
.switch-control__option[aria-checked='true']:not(:focus-visible) .switch-control__visual,
|
|
14230
|
+
.page-feedback__option[aria-pressed='true']:not(:focus-visible) {
|
|
14231
|
+
background: var(--q1-color-bg-subtle);
|
|
14232
|
+
box-shadow: inset 0 0 0 calc(var(--q1-space-1) / 4) var(--q1-color-border-strong);
|
|
14233
|
+
}
|
|
14234
|
+
|
|
14235
|
+
/* JSON View 高亮行保留左竖条形态,颜色与底色走同一配方。 */
|
|
14236
|
+
.q1-json-view__row--highlight {
|
|
14237
|
+
background: var(--q1-color-bg-subtle);
|
|
14238
|
+
box-shadow: inset calc(var(--q1-space-1) / 2) 0 0 var(--q1-color-border-strong);
|
|
14239
|
+
}
|
|
14240
|
+
|
|
14241
|
+
/* 暗色选中浅底:bg-subtle 在暗色是 gray-50,比面板更深,改用抬升一档的 bg-raised,覆盖上面全部选中态。 */
|
|
14242
|
+
:root[data-theme='dark'] :is(
|
|
14243
|
+
.choicebox:has(.choicebox__input:checked:not(:disabled)),
|
|
14244
|
+
.pagination__button--current:not(:disabled, [aria-disabled='true']),
|
|
14245
|
+
.toggle-group__item[aria-pressed='true']:not(:focus-visible),
|
|
14246
|
+
.switch-control__option[aria-checked='true']:not(:focus-visible) .switch-control__visual,
|
|
14247
|
+
.page-feedback__option[aria-pressed='true']:not(:focus-visible),
|
|
14248
|
+
.q1-json-view__row--highlight
|
|
14249
|
+
) {
|
|
14250
|
+
background: var(--q1-color-bg-raised);
|
|
14251
|
+
}
|
package/dist-pkg/tokens.css
CHANGED
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
--q1-color-bg-media: #0a0a0a;
|
|
134
134
|
--q1-color-text-on-media: #ffffff;
|
|
135
135
|
--q1-color-border: var(--q1-color-gray-alpha-200);
|
|
136
|
-
--q1-color-border-control: var(--q1-color-
|
|
136
|
+
--q1-color-border-control: var(--q1-color-gray-700);
|
|
137
137
|
--q1-color-border-subtle: rgba(203, 213, 225, 0.82);
|
|
138
138
|
--q1-color-border-strong: var(--q1-color-gray-500);
|
|
139
139
|
--q1-color-border-active: var(--q1-color-primary-700);
|
|
@@ -170,15 +170,23 @@
|
|
|
170
170
|
--q1-color-auth-border-strong: #d6dde6;
|
|
171
171
|
--q1-color-auth-surface-card: #ffffff;
|
|
172
172
|
--q1-color-auth-surface-subtle: #f8fafc;
|
|
173
|
+
--q1-color-auth-surface-input: #ffffff;
|
|
174
|
+
--q1-color-auth-surface-hover: #f6f9fc;
|
|
173
175
|
--q1-color-auth-surface-disabled: #f6f9fc;
|
|
174
176
|
--q1-color-auth-surface-media: #04050a;
|
|
177
|
+
--q1-color-auth-action-hover: #4434d4;
|
|
178
|
+
--q1-color-auth-action-press: #2e2b8c;
|
|
175
179
|
--q1-color-auth-action-disabled-bg: #c7c9cc;
|
|
176
180
|
--q1-color-auth-action-disabled-border: #c7c9cc;
|
|
177
181
|
--q1-color-auth-action-disabled-text: #ffffff;
|
|
178
182
|
--q1-color-auth-focus-ring: rgba(83, 58, 253, 0.42);
|
|
179
183
|
--q1-color-auth-danger-border: #d92d20;
|
|
180
184
|
--q1-color-auth-danger-surface: #fff8f7;
|
|
185
|
+
--q1-color-auth-error-text: #be123c;
|
|
181
186
|
--q1-color-auth-field-error-text: #d32f2f;
|
|
187
|
+
--q1-color-auth-notice-border: #b9b9f9;
|
|
188
|
+
--q1-color-auth-notice-surface: #efebff;
|
|
189
|
+
--q1-color-auth-notice-text: #2e2b8c;
|
|
182
190
|
--q1-color-auth-media-overlay: rgba(4, 5, 10, 0.46);
|
|
183
191
|
--q1-color-auth-media-overlay-hover: rgba(4, 5, 10, 0.66);
|
|
184
192
|
--q1-color-auth-on-media-border: rgba(255, 255, 255, 0.22);
|
|
@@ -450,7 +458,7 @@
|
|
|
450
458
|
--q1-color-border: var(--q1-color-gray-400);
|
|
451
459
|
--q1-color-border-control: var(--q1-color-gray-600);
|
|
452
460
|
--q1-color-border-subtle: #2a2a2a;
|
|
453
|
-
--q1-color-border-strong: var(--q1-color-gray-
|
|
461
|
+
--q1-color-border-strong: var(--q1-color-gray-500);
|
|
454
462
|
--q1-color-border-active: var(--q1-color-primary-900);
|
|
455
463
|
--q1-color-text-primary: var(--q1-color-gray-1000);
|
|
456
464
|
--q1-color-text-secondary: var(--q1-color-gray-900);
|
|
@@ -485,15 +493,23 @@
|
|
|
485
493
|
--q1-color-auth-border-strong: #343943;
|
|
486
494
|
--q1-color-auth-surface-card: #17191d;
|
|
487
495
|
--q1-color-auth-surface-subtle: #101216;
|
|
496
|
+
--q1-color-auth-surface-input: #101216;
|
|
497
|
+
--q1-color-auth-surface-hover: #202329;
|
|
488
498
|
--q1-color-auth-surface-disabled: #202329;
|
|
489
499
|
--q1-color-auth-surface-media: #04050a;
|
|
500
|
+
--q1-color-auth-action-hover: #4434d4;
|
|
501
|
+
--q1-color-auth-action-press: #2e2b8c;
|
|
490
502
|
--q1-color-auth-action-disabled-bg: #3b404a;
|
|
491
503
|
--q1-color-auth-action-disabled-border: #3b404a;
|
|
492
504
|
--q1-color-auth-action-disabled-text: #a6acb8;
|
|
493
505
|
--q1-color-auth-focus-ring: rgba(126, 108, 255, 0.52);
|
|
494
506
|
--q1-color-auth-danger-border: #d92d20;
|
|
495
507
|
--q1-color-auth-danger-surface: #241719;
|
|
508
|
+
--q1-color-auth-error-text: #fb7185;
|
|
496
509
|
--q1-color-auth-field-error-text: #d32f2f;
|
|
510
|
+
--q1-color-auth-notice-border: #3d3794;
|
|
511
|
+
--q1-color-auth-notice-surface: #1d1b3a;
|
|
512
|
+
--q1-color-auth-notice-text: #cbc4ff;
|
|
497
513
|
--q1-color-auth-media-overlay: rgba(4, 5, 10, 0.46);
|
|
498
514
|
--q1-color-auth-media-overlay-hover: rgba(4, 5, 10, 0.66);
|
|
499
515
|
--q1-color-auth-on-media-border: rgba(255, 255, 255, 0.22);
|
|
@@ -627,3 +643,33 @@
|
|
|
627
643
|
--q1-z-overlay-root: 1300;
|
|
628
644
|
--q1-z-toast: 1400;
|
|
629
645
|
}
|
|
646
|
+
|
|
647
|
+
[data-q1-theme='light'] {
|
|
648
|
+
--q1-color-auth-text-primary: #061b31;
|
|
649
|
+
--q1-color-auth-text-secondary: #273951;
|
|
650
|
+
--q1-color-auth-text-muted: #64748d;
|
|
651
|
+
--q1-color-auth-border-strong: #d6dde6;
|
|
652
|
+
--q1-color-auth-surface-card: #ffffff;
|
|
653
|
+
--q1-color-auth-surface-subtle: #f8fafc;
|
|
654
|
+
--q1-color-auth-surface-input: #ffffff;
|
|
655
|
+
--q1-color-auth-surface-hover: #f6f9fc;
|
|
656
|
+
--q1-color-auth-surface-disabled: #f6f9fc;
|
|
657
|
+
--q1-color-auth-surface-media: #04050a;
|
|
658
|
+
--q1-color-auth-action-hover: #4434d4;
|
|
659
|
+
--q1-color-auth-action-press: #2e2b8c;
|
|
660
|
+
--q1-color-auth-action-disabled-bg: #c7c9cc;
|
|
661
|
+
--q1-color-auth-action-disabled-border: #c7c9cc;
|
|
662
|
+
--q1-color-auth-action-disabled-text: #ffffff;
|
|
663
|
+
--q1-color-auth-focus-ring: rgba(83, 58, 253, 0.42);
|
|
664
|
+
--q1-color-auth-danger-border: #d92d20;
|
|
665
|
+
--q1-color-auth-danger-surface: #fff8f7;
|
|
666
|
+
--q1-color-auth-error-text: #be123c;
|
|
667
|
+
--q1-color-auth-field-error-text: #d32f2f;
|
|
668
|
+
--q1-color-auth-notice-border: #b9b9f9;
|
|
669
|
+
--q1-color-auth-notice-surface: #efebff;
|
|
670
|
+
--q1-color-auth-notice-text: #2e2b8c;
|
|
671
|
+
--q1-color-auth-media-overlay: rgba(4, 5, 10, 0.46);
|
|
672
|
+
--q1-color-auth-media-overlay-hover: rgba(4, 5, 10, 0.66);
|
|
673
|
+
--q1-color-auth-on-media-border: rgba(255, 255, 255, 0.22);
|
|
674
|
+
--q1-color-auth-on-media-border-hover: rgba(255, 255, 255, 0.34);
|
|
675
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q1travel/design-system",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"description": "React components, design tokens, and interface contracts for q1travel products.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"check:shell": "npm run sync:contracts && node scripts/check-shell.mjs && node scripts/check-token-package.mjs && npm run check:release-provenance",
|
|
55
55
|
"check:browser:strictmode": "node scripts/check-browser.mjs --strictmode-ref",
|
|
56
56
|
"check:browser": "node --test scripts/devtools-connection.test.mjs && npm run check:browser:strictmode && node scripts/check-browser.mjs --note && node scripts/check-browser.mjs && node scripts/check-clearable-input-browser.mjs && node scripts/check-calendar-browser.mjs && node scripts/check-banner-browser.mjs && node scripts/check-button-link-browser.mjs && node scripts/check-button-browser.mjs && node scripts/check-browser-component-browser.mjs",
|
|
57
|
-
"check:package-api": "node scripts/check-package-api.mjs && node --test scripts/check-q1-design-lint.test.mjs && node --test scripts/check-json-view-contract.test.mjs && node --test scripts/check-verification-code-input.test.mjs && node --test scripts/check-bare-field-error.test.mjs scripts/check-label-hidden-contract.test.mjs && node --test scripts/check-controls-contract.test.mjs && node --test scripts/check-pagination-contract.test.mjs && node --test scripts/check-table-core.test.mjs scripts/check-table-interaction.test.mjs scripts/check-table-layout.test.mjs && node --test scripts/check-contrast-contract.test.mjs && node --test scripts/check-optical-alignment-contract.test.mjs && node --test scripts/check-overlay-coordination.test.mjs scripts/check-sheet-contract.test.mjs && node --test scripts/check-choicebox-contract.test.mjs && node --test scripts/check-clearable-input-contract.test.mjs && node --test scripts/check-card-contract.test.mjs && node --test scripts/check-book-contract.test.mjs && node --test scripts/check-breadcrumbs-contract.test.mjs && node --test scripts/check-description-contract.test.mjs && node --test scripts/check-error-message-contract.test.mjs && node --test scripts/check-show-more-contract.test.mjs && node --test scripts/check-load-more-button-contract.test.mjs && node --test scripts/check-banner-contract.test.mjs && node --test scripts/check-project-banner-contract.test.mjs && node --test scripts/check-note-contract.test.mjs && node --test scripts/check-feedback-contract.test.mjs && node --test scripts/check-page-feedback-contract.test.mjs && node --test scripts/check-drawer-gesture.test.mjs && node --test scripts/check-checkbox-circle-contract.test.mjs && node --test scripts/check-chat-contract.test.mjs && node --test scripts/check-calendar-single-contract.test.mjs && node --test scripts/check-hover-card-contract.test.mjs && node --test scripts/check-bare-field-error.test.mjs && node --test scripts/check-controls-contract.test.mjs scripts/check-control-size-contract.test.mjs && node --test scripts/check-composer-dots-menu-contract.test.mjs && node --test scripts/check-filter-option-panel-contract.test.mjs",
|
|
57
|
+
"check:package-api": "node scripts/check-package-api.mjs && node --test scripts/check-q1-design-lint.test.mjs scripts/check-q1-design-bin-entry.test.mjs && node --test scripts/check-json-view-contract.test.mjs && node --test scripts/check-verification-code-input.test.mjs && node --test scripts/check-bare-field-error.test.mjs scripts/check-label-hidden-contract.test.mjs && node --test scripts/check-controls-contract.test.mjs && node --test scripts/check-pagination-contract.test.mjs && node --test scripts/check-table-core.test.mjs scripts/check-table-interaction.test.mjs scripts/check-table-layout.test.mjs && node --test scripts/check-contrast-contract.test.mjs && node --test scripts/check-optical-alignment-contract.test.mjs && node --test scripts/check-overlay-coordination.test.mjs scripts/check-sheet-contract.test.mjs && node --test scripts/check-choicebox-contract.test.mjs && node --test scripts/check-clearable-input-contract.test.mjs && node --test scripts/check-card-contract.test.mjs && node --test scripts/check-book-contract.test.mjs && node --test scripts/check-breadcrumbs-contract.test.mjs && node --test scripts/check-description-contract.test.mjs && node --test scripts/check-error-message-contract.test.mjs && node --test scripts/check-show-more-contract.test.mjs && node --test scripts/check-load-more-button-contract.test.mjs && node --test scripts/check-banner-contract.test.mjs && node --test scripts/check-project-banner-contract.test.mjs && node --test scripts/check-note-contract.test.mjs && node --test scripts/check-feedback-contract.test.mjs && node --test scripts/check-page-feedback-contract.test.mjs && node --test scripts/check-drawer-gesture.test.mjs && node --test scripts/check-checkbox-circle-contract.test.mjs && node --test scripts/check-chat-contract.test.mjs && node --test scripts/check-calendar-single-contract.test.mjs && node --test scripts/check-hover-card-contract.test.mjs && node --test scripts/check-bare-field-error.test.mjs && node --test scripts/check-controls-contract.test.mjs scripts/check-control-size-contract.test.mjs && node --test scripts/check-composer-dots-menu-contract.test.mjs && node --test scripts/check-filter-option-panel-contract.test.mjs",
|
|
58
58
|
"check:release-provenance": "node --test scripts/check-release-provenance.test.mjs && node scripts/check-release-provenance.mjs",
|
|
59
59
|
"check:release-sync": "node --test scripts/release-sync.test.mjs",
|
|
60
60
|
"release:verify": "node scripts/release-sync.mjs --verify-only",
|