@carissa-io/theme-default 0.3.14 → 0.3.16
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/DESIGN.md +23 -0
- package/README.md +18 -2
- package/dist/index.css +291 -0
- package/package.json +27 -2
- package/scss/_modes.scss +21 -1
- package/scss/_tokens.scss +4 -0
- package/scss/index.scss +84 -0
package/DESIGN.md
CHANGED
|
@@ -49,6 +49,8 @@ Both modes ship in the same package; dark is **opt-in per app** with `data-mode`
|
|
|
49
49
|
| `warning` | `#a16207` | `#fbbf24` |
|
|
50
50
|
| `info` | `#1d4ed8` | `#60a5fa` |
|
|
51
51
|
| `focus` | `#000000` | `#ffffff` |
|
|
52
|
+
| `scrollbar-thumb` | `#737373` | `#737373` |
|
|
53
|
+
| `scrollbar-track` | `#ffffff` | `#0a0a0a` |
|
|
52
54
|
|
|
53
55
|
Decisions: dark inverts the contrast of the light theme, it does not invent a new look (near-black ground, the primary action becomes white,
|
|
54
56
|
square corners and the 1px focus ring stay). The ground is `#0a0a0a`, not pure black, so surfaces (`#171717`) can be told apart. Primary and focus
|
|
@@ -74,6 +76,7 @@ Contrast (WCAG 2.x): text 4.5:1, borders and the focus ring 3:1.
|
|
|
74
76
|
| `info` on `background` / on `surface` | 6.70 / 6.15 | 7.79 / 7.05 |
|
|
75
77
|
| `error` on `surface` | 4.15 | 6.48 |
|
|
76
78
|
| `disabled-text` on `surface` (disabled, exempt) | 2.31 | 3.78 |
|
|
79
|
+
| `scrollbar-thumb` on `scrollbar-track` / on `background` (the same colour) | 4.74 | 4.18 |
|
|
77
80
|
|
|
78
81
|
The light `error` on `surface` (4.15) is below 4.5 for text, so the Alert never sets text in the status color (only the icon and the accent border, which need 3:1).
|
|
79
82
|
|
|
@@ -81,6 +84,11 @@ Every `muted` text pairing passes AA in both modes (a test asserts it): the ligh
|
|
|
81
84
|
`surface-active`, so it is now `#666666` (5.74 / 5.27 / 4.56). The disabled states are exempt from WCAG but are listed in the backlog for a contrast review. Known gap in dark: the 0-width listbox option separator keeps the component default
|
|
82
85
|
(`#dee2e6`); it draws nothing today.
|
|
83
86
|
|
|
87
|
+
## Scrollbars
|
|
88
|
+
Two rules theme every scroll container, and no component carries a scrollbar rule: `scrollbar-color: var(--c-scrollbar-thumb) var(--c-scrollbar-track)` on `:root` (inherited, so every scroller gets it) and `scrollbar-width: thin` on `*`. The thumb is the neutral 500 (`#737373`) in both modes; the track is the page ground, equal to `background` (`#ffffff` / `#0a0a0a`), so the scrollbar is just a thumb on the page and no extra rail is drawn. The thumb passes the 3:1 non-text minimum against the track and the background (4.74 / 4.18, the same pair) and also on `surface` (4.35 / 3.78) and the `surface-active` hover fill (3.76 / 3.19); a test asserts that the track equals the background and the ratio in each mode. Known trade-off: inside a surface-coloured container (the sidebar, a card) the track shows as a strip of the page colour; a theme can override `$scrollbar-track` (and the dark role) with its surface value. The width is `thin` (the `$scrollbar-width` token), which suits the Swiss restraint and still leaves a target the pointer can grab; touch and overlay scrollbars ignore it.
|
|
89
|
+
|
|
90
|
+
Known limits: `scrollbar-width` is not an inherited property, so a `:root` declaration would thin only the page; it is declared with a universal rule instead, which has zero specificity so it never overrides a component. The rules use the standard properties only, so there is colour and thickness but no rounding, no arrows and no hover state (those need the `::-webkit-scrollbar` pseudo-elements, which the theme deliberately does not use). Support is Chrome 121+, Firefox 64+ and Safari 18.2+; older browsers keep the native bar, which stays usable. A scroller that sets its own `scrollbar-width` keeps it, whatever the order: the carousel hides its bar on purpose (`scrollbar-width: none`, a class selector) and the theme does not undo it.
|
|
91
|
+
|
|
84
92
|
## Typography
|
|
85
93
|
Configured through [components-typography](components/components-typography): `Heading` (the element comes from `level`, the look from `size`)
|
|
86
94
|
and `Text`. Neutral grotesque, no margins (spacing belongs to the layout), color from `$text`; only muted text uses `$muted`.
|
|
@@ -451,6 +459,21 @@ From [components-theme-switch](components/components-theme-switch): `ThemeSwitch
|
|
|
451
459
|
## Icon
|
|
452
460
|
From [components-icon](components/components-icon): `Icon`. No colour of its own: the stroke is `currentColor`, so an icon has the contrast of the text it sits in (17.93:1 / 18.16:1 for `--c-text`, 5.74:1 / 7.85:1 for `--c-muted`, both above the 3:1 non-text minimum). Sizes are pinned in rem (1, 1.25, 1.5 and 2rem) so they do not drift with the font size around them. A decorative icon is `aria-hidden`; a lone one needs a `label`.
|
|
453
461
|
|
|
462
|
+
## AI label
|
|
463
|
+
From [components-ai-label](components/components-ai-label): `AiLabel`. The mark is the neutral Badge (`--c-surface` with a `--c-muted` border, `--c-text` text at 16.44:1, see [Badge](#badge)); the optional details line is `--c-muted` text (5.74:1 / 7.85:1 on the background, 5.27:1 / 7.11:1 on `--c-surface`) at the caption size next to a `sm` mark and the small size next to a `md` one, with 0.5rem between the two. It is text beside the mark: nothing opens and nothing needs a keyboard.
|
|
464
|
+
|
|
465
|
+
## Thinking indicator
|
|
466
|
+
From [components-thinking-indicator](components/components-thinking-indicator): `ThinkingIndicator`. Three 0.375rem dots in `currentColor` of an indicator whose text is `--c-muted` (5.74:1 / 7.85:1 on the background, 5.27:1 / 7.11:1 on `--c-surface`), with the label in the small body size. The dots are decorative (the label is the accessible text), so their pulsing opacity (0.25 to 1) carries no contrast requirement; they keep the component's per-state timing (`queued` slowest, `working` fastest) and stand still at 0.6 opacity under `prefers-reduced-motion`. The dots are round, the only round shape of the set: they are not a control.
|
|
467
|
+
|
|
468
|
+
## Streaming text
|
|
469
|
+
From [components-streaming-text](components/components-streaming-text): `StreamingText`. The text is `--c-text` (17.93:1 / 18.16:1 on the background, 16.44:1 on `--c-surface`) at the body line height (1.5). The caret is a 0.125em wide square bar in `currentColor`, so it always has the contrast of the text it follows and follows both modes with no colour of its own; it blinks at 1s and is steady under `prefers-reduced-motion`. The caret is `aria-hidden` and the text never announces itself.
|
|
470
|
+
|
|
471
|
+
## Message
|
|
472
|
+
From [components-message](components/components-message): `Message`. The roles are told apart by more than colour: the **user's** message sits at the end of the column on a `--c-surface-active` bubble, the **assistant's** at the start on a `--c-surface` bubble with a 1px `--c-surface-active` border, and the **system's** is centred, unframed and `--c-muted`; every message also shows its author name as a bold caption in `--c-muted` (a visible text cue that does not depend on colour or alignment). Contrast: `--c-text` is 14.23:1 / 13.88:1 on the user bubble and 16.44:1 / 16.44:1 on the assistant one; the name is 4.56:1 / 6.00:1 on the user bubble and 5.27:1 / 7.11:1 on the assistant one; the system text is 5.74:1 / 7.85:1 on the background. The bubbles are square, like the cards, with 0.75rem 1rem of padding, and the bubble fills sit at 1.26:1 / 1.31:1 (`--c-surface-active`) and 1.09:1 / 1.10:1 (`--c-surface`) against the background, so they are a quiet cue and never the only one. A **stopped** note is `--c-muted` text. A **failed** note is a sentence (the mapped error code, never the server's text) in `--c-error` (4.53:1 / 7.16:1) inside a 1px `--c-error` frame (3:1 or more as a graphic) on a `--c-background` fill, so the raw light `error` is not set on the grey bubble, where it would be 4.15:1; it is not colour alone because the sentence says what happened. Actions sit under the content, 0.25rem apart.
|
|
473
|
+
|
|
474
|
+
## Message list
|
|
475
|
+
From [components-message-list](components/components-message-list): `MessageList`. A scrolling log with no fill of its own: it sits on the page background and the bubbles carry the contrast. Messages are 1rem apart inside 1rem of padding. The log takes keyboard focus so it can be scrolled, and its ring is the theme's 1px `--c-focus` outline drawn inside the box (21:1 / 19.8:1 on the background). The "Jump to latest" button is the secondary Button (see [Buttons](#buttons)) centred on the bottom edge and floating 1rem above it; it appears only when the reader scrolled up. The `empty` slot is an `EmptyState` with 2rem of padding (keep the list on `--c-background`, see [Empty state](#empty-state)). No scroll shadow: the component has no variable for it.
|
|
476
|
+
|
|
454
477
|
## Keyboard focus (all controls)
|
|
455
478
|
One rule keeps the controls coherent: **an outlined control thickens its own border from 1px to 2px, black** (text fields,
|
|
456
479
|
select, combobox, date picker, textarea, upload, empty checkbox, off switch, radio, secondary and ghost buttons); **a filled
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ peer dependencies).
|
|
|
29
29
|
## Color modes
|
|
30
30
|
The theme has a light and a dark palette in one package. The palette is a set of CSS custom properties (`--c-background`, `--c-text`,
|
|
31
31
|
`--c-muted`, `--c-surface`, `--c-surface-active`, `--c-primary`, `--c-on-primary`, `--c-primary-hover`, `--c-primary-active`,
|
|
32
|
-
`--c-disabled-text`, `--c-disabled-border`, `--c-error`, `--c-success`, `--c-warning`, `--c-info`, `--c-focus`) that every component reads. **Dark is opt-in per app**: without
|
|
32
|
+
`--c-disabled-text`, `--c-disabled-border`, `--c-error`, `--c-success`, `--c-warning`, `--c-info`, `--c-focus`, `--c-scrollbar-thumb`, `--c-scrollbar-track`) that every component reads. **Dark is opt-in per app**: without
|
|
33
33
|
the attribute below the app is light, exactly as before.
|
|
34
34
|
|
|
35
35
|
An app opts in with one line in the file where it imports the theme (`App/theme.js`), setting the mode on `<html>`:
|
|
@@ -63,6 +63,19 @@ first paint: put this in the `<head>` of the page, before the stylesheet, with t
|
|
|
63
63
|
The Sass tokens above stay the static **light** values (for Sass consumers); the dark values are in `scss/_modes.scss`. The page background and
|
|
64
64
|
text color are the app's: give `body` `background: var(--c-background); color: var(--c-text)`.
|
|
65
65
|
|
|
66
|
+
## Scrollbars
|
|
67
|
+
The theme styles every scroll container with no change in any component: the page, the message list, the table wrapper, the modal and
|
|
68
|
+
drawer bodies, the select, combobox and listbox popups, the sidebar and code blocks. It is two rules: `scrollbar-color: var(--c-scrollbar-thumb)
|
|
69
|
+
var(--c-scrollbar-track)` on `:root` (the property is inherited) and `scrollbar-width: thin` on `*`, because `scrollbar-width` is not inherited and so
|
|
70
|
+
has to be declared on every element. The `*` rule has zero specificity, so any component rule or your own rule wins over it. The colors
|
|
71
|
+
are two palette roles, so they follow `data-mode`; the thickness is the `$scrollbar-width` token.
|
|
72
|
+
|
|
73
|
+
To change them in your own theme, set `$scrollbar-thumb`, `$scrollbar-track` and `$scrollbar-width` in your `scss/_tokens.scss` (and the
|
|
74
|
+
dark values of the two roles in `scss/_modes.scss`); keep the thumb at 3:1 or more against the track and against the background. The default track is the page background, so inside a surface-coloured container (the sidebar, a card) it shows as a strip of the page colour; a theme that prefers it quieter there sets `$scrollbar-track` to its surface value. An app that
|
|
75
|
+
only needs one scroller to look different overrides the two properties on that element (`scrollbar-color`, `scrollbar-width`); the carousel
|
|
76
|
+
already hides its own bar. Browsers without the standard properties (before Chrome 121, Firefox 64 or Safari 18.2) keep the native bar. See
|
|
77
|
+
[DESIGN.md](DESIGN.md) for the values and limits.
|
|
78
|
+
|
|
66
79
|
## Tokens
|
|
67
80
|
The palette and the values shared by every component are Sass variables in `scss/_tokens.scss`, the single source of
|
|
68
81
|
the theme's values (`scss/index.scss` reads them with `@use 'tokens' as *`). The file emits no CSS, so an app can read
|
|
@@ -89,6 +102,9 @@ change.
|
|
|
89
102
|
| `$success` | `#15803d` | success accent and icon (Alert) |
|
|
90
103
|
| `$warning` | `#a16207` | warning accent and icon (Alert) |
|
|
91
104
|
| `$info` | `#1d4ed8` | info accent and icon (Alert) |
|
|
105
|
+
| `$scrollbar-thumb` | `#737373` | scrollbar thumb |
|
|
106
|
+
| `$scrollbar-track` | `$white` | scrollbar track (the page ground) |
|
|
107
|
+
| `$scrollbar-width` | `thin` | scrollbar thickness |
|
|
92
108
|
| `$font-family` | `'Helvetica Neue', Helvetica, Arial, 'Liberation Sans', sans-serif` | type stack |
|
|
93
109
|
| `$font-family-mono` | `ui-monospace, 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace` | monospace stack for system data and code |
|
|
94
110
|
| `$radius` | `0` | corner radius |
|
|
@@ -142,7 +158,7 @@ it: check the names against the theme's `scss/_tokens.scss` before you switch.
|
|
|
142
158
|
|
|
143
159
|
## What it configures
|
|
144
160
|
`components-input`, `-button`, `-checkbox`, `-switch`, `-radio-group`, `-slider`, `-popover`, `-listbox`, `-select`,
|
|
145
|
-
`-combobox`, `-upload`, `-calendar`, `-date-picker`, `-search`, `-typography`, `-card`, `-alert`, `-spinner`, `-modal`, `-toast`, `-divider`, `-badge`, `-tag`, `-link`, `-avatar`, `-skeleton`, `-progress`, `-tooltip`, `-prose`, `-tabs`, `-accordion`, `-breadcrumb`, `-pagination`, `-stepper`, `-sidebar`, `-navbar`, `-drawer`, `-menu`, `-table`, `-list`, `-description-list`, `-stat`, `-empty-state`, `-timeline`, `-tree`, `-code`, `-field`, `-number-input`, `-otp-input`, `-phone`, `-tag-input`, `-time-picker`, `-date-range`, `-rating`, `-color-picker`, `-async-autocomplete`, `-section`, `-faq`, `-carousel`, `-visually-hidden`, `-live-region`, `-theme-switch` and `-
|
|
161
|
+
`-combobox`, `-upload`, `-calendar`, `-date-picker`, `-search`, `-typography`, `-card`, `-alert`, `-spinner`, `-modal`, `-toast`, `-divider`, `-badge`, `-tag`, `-link`, `-avatar`, `-skeleton`, `-progress`, `-tooltip`, `-prose`, `-tabs`, `-accordion`, `-breadcrumb`, `-pagination`, `-stepper`, `-sidebar`, `-navbar`, `-drawer`, `-menu`, `-table`, `-list`, `-description-list`, `-stat`, `-empty-state`, `-timeline`, `-tree`, `-code`, `-field`, `-number-input`, `-otp-input`, `-phone`, `-tag-input`, `-time-picker`, `-date-range`, `-rating`, `-color-picker`, `-async-autocomplete`, `-section`, `-faq`, `-carousel`, `-visually-hidden`, `-live-region`, `-theme-switch`, `-icon`, `-ai-label`, `-thinking-indicator`, `-streaming-text`, `-message` and `-message-list`. `components-email`, `-password`, `-url` and `-textarea`
|
|
146
162
|
are styled through the input.
|
|
147
163
|
|
|
148
164
|
The checkbox's mixed state (`indeterminate`, from the next `components-checkbox` release, class `c-checkbox--indeterminate`) is the checked box with a 10px dash in `--c-on-primary`; on an older checkbox the rule is simply unused.
|
package/dist/index.css
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
--c-warning: #a16207;
|
|
17
17
|
--c-info: #1d4ed8;
|
|
18
18
|
--c-focus: #000;
|
|
19
|
+
--c-scrollbar-thumb: #737373;
|
|
20
|
+
--c-scrollbar-track: #fff;
|
|
19
21
|
color-scheme: light;
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -36,6 +38,8 @@
|
|
|
36
38
|
--c-warning: #a16207;
|
|
37
39
|
--c-info: #1d4ed8;
|
|
38
40
|
--c-focus: #000;
|
|
41
|
+
--c-scrollbar-thumb: #737373;
|
|
42
|
+
--c-scrollbar-track: #fff;
|
|
39
43
|
color-scheme: light;
|
|
40
44
|
}
|
|
41
45
|
|
|
@@ -56,6 +60,8 @@
|
|
|
56
60
|
--c-warning: #fbbf24;
|
|
57
61
|
--c-info: #60a5fa;
|
|
58
62
|
--c-focus: #fff;
|
|
63
|
+
--c-scrollbar-thumb: #737373;
|
|
64
|
+
--c-scrollbar-track: #0a0a0a;
|
|
59
65
|
color-scheme: dark;
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -77,9 +83,19 @@
|
|
|
77
83
|
--c-warning: #fbbf24;
|
|
78
84
|
--c-info: #60a5fa;
|
|
79
85
|
--c-focus: #fff;
|
|
86
|
+
--c-scrollbar-thumb: #737373;
|
|
87
|
+
--c-scrollbar-track: #0a0a0a;
|
|
80
88
|
color-scheme: dark;
|
|
81
89
|
}
|
|
82
90
|
}
|
|
91
|
+
:root {
|
|
92
|
+
scrollbar-color: var(--c-scrollbar-thumb) var(--c-scrollbar-track);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
* {
|
|
96
|
+
scrollbar-width: thin;
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
.c-input {
|
|
84
100
|
display: block;
|
|
85
101
|
font-family: "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif;
|
|
@@ -5456,3 +5472,278 @@ button.c-theme-switch:focus-visible {
|
|
|
5456
5472
|
width: 2rem;
|
|
5457
5473
|
height: 2rem;
|
|
5458
5474
|
}
|
|
5475
|
+
|
|
5476
|
+
.c-ai-label {
|
|
5477
|
+
display: inline-flex;
|
|
5478
|
+
flex-wrap: wrap;
|
|
5479
|
+
gap: 0.5rem;
|
|
5480
|
+
align-items: center;
|
|
5481
|
+
vertical-align: middle;
|
|
5482
|
+
}
|
|
5483
|
+
|
|
5484
|
+
.c-ai-label__details {
|
|
5485
|
+
color: var(--c-muted);
|
|
5486
|
+
}
|
|
5487
|
+
|
|
5488
|
+
.c-ai-label--sm .c-ai-label__details {
|
|
5489
|
+
font-size: 0.75rem;
|
|
5490
|
+
}
|
|
5491
|
+
|
|
5492
|
+
.c-ai-label--md .c-ai-label__details {
|
|
5493
|
+
font-size: 0.875rem;
|
|
5494
|
+
}
|
|
5495
|
+
|
|
5496
|
+
@keyframes c-thinking-indicator-dot {
|
|
5497
|
+
0%, 80%, 100% {
|
|
5498
|
+
opacity: 0.25;
|
|
5499
|
+
transform: scale(0.8);
|
|
5500
|
+
}
|
|
5501
|
+
40% {
|
|
5502
|
+
opacity: 1;
|
|
5503
|
+
transform: scale(1);
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
.c-thinking-indicator {
|
|
5507
|
+
--c-thinking-indicator-duration: 1.2s;
|
|
5508
|
+
display: inline-flex;
|
|
5509
|
+
gap: 0.5rem;
|
|
5510
|
+
align-items: center;
|
|
5511
|
+
font-size: 0.875rem;
|
|
5512
|
+
color: var(--c-muted);
|
|
5513
|
+
}
|
|
5514
|
+
|
|
5515
|
+
.c-thinking-indicator--queued {
|
|
5516
|
+
--c-thinking-indicator-duration: 2.4s;
|
|
5517
|
+
}
|
|
5518
|
+
|
|
5519
|
+
.c-thinking-indicator--typing {
|
|
5520
|
+
--c-thinking-indicator-duration: 1.2s;
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5523
|
+
.c-thinking-indicator--reasoning {
|
|
5524
|
+
--c-thinking-indicator-duration: 1.8s;
|
|
5525
|
+
}
|
|
5526
|
+
|
|
5527
|
+
.c-thinking-indicator--working {
|
|
5528
|
+
--c-thinking-indicator-duration: 0.9s;
|
|
5529
|
+
}
|
|
5530
|
+
|
|
5531
|
+
.c-thinking-indicator__dots {
|
|
5532
|
+
display: inline-flex;
|
|
5533
|
+
gap: 0.25rem;
|
|
5534
|
+
align-items: center;
|
|
5535
|
+
}
|
|
5536
|
+
|
|
5537
|
+
.c-thinking-indicator__dot {
|
|
5538
|
+
display: block;
|
|
5539
|
+
width: 0.375rem;
|
|
5540
|
+
height: 0.375rem;
|
|
5541
|
+
background: currentColor;
|
|
5542
|
+
border-radius: 50%;
|
|
5543
|
+
opacity: 0.25;
|
|
5544
|
+
animation: c-thinking-indicator-dot var(--c-thinking-indicator-duration) ease-in-out infinite;
|
|
5545
|
+
}
|
|
5546
|
+
.c-thinking-indicator__dot:nth-child(2) {
|
|
5547
|
+
animation-delay: 0.15s;
|
|
5548
|
+
}
|
|
5549
|
+
.c-thinking-indicator__dot:nth-child(3) {
|
|
5550
|
+
animation-delay: 0.3s;
|
|
5551
|
+
}
|
|
5552
|
+
@media (prefers-reduced-motion: reduce) {
|
|
5553
|
+
.c-thinking-indicator__dot {
|
|
5554
|
+
opacity: 0.6;
|
|
5555
|
+
animation: none;
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
.c-thinking-indicator__label--hidden {
|
|
5560
|
+
position: absolute;
|
|
5561
|
+
width: 1px;
|
|
5562
|
+
height: 1px;
|
|
5563
|
+
padding: 0;
|
|
5564
|
+
margin: -1px;
|
|
5565
|
+
overflow: hidden;
|
|
5566
|
+
white-space: nowrap;
|
|
5567
|
+
border: 0;
|
|
5568
|
+
clip-path: inset(50%);
|
|
5569
|
+
}
|
|
5570
|
+
|
|
5571
|
+
@keyframes c-streaming-text-caret {
|
|
5572
|
+
0%, 45% {
|
|
5573
|
+
opacity: 1;
|
|
5574
|
+
}
|
|
5575
|
+
55%, 100% {
|
|
5576
|
+
opacity: 0;
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
.c-streaming-text {
|
|
5580
|
+
color: var(--c-text);
|
|
5581
|
+
overflow-wrap: anywhere;
|
|
5582
|
+
}
|
|
5583
|
+
|
|
5584
|
+
.c-streaming-text--plain {
|
|
5585
|
+
line-height: 1.5;
|
|
5586
|
+
white-space: pre-wrap;
|
|
5587
|
+
}
|
|
5588
|
+
|
|
5589
|
+
.c-streaming-text__caret {
|
|
5590
|
+
display: inline-block;
|
|
5591
|
+
width: 0.125em;
|
|
5592
|
+
height: 1.1em;
|
|
5593
|
+
margin-inline-start: 0.125em;
|
|
5594
|
+
vertical-align: text-bottom;
|
|
5595
|
+
background: currentColor;
|
|
5596
|
+
border-radius: 0;
|
|
5597
|
+
animation: c-streaming-text-caret 1s step-end infinite;
|
|
5598
|
+
}
|
|
5599
|
+
@media (prefers-reduced-motion: reduce) {
|
|
5600
|
+
.c-streaming-text__caret {
|
|
5601
|
+
animation: none;
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5605
|
+
.c-message {
|
|
5606
|
+
box-sizing: border-box;
|
|
5607
|
+
display: flex;
|
|
5608
|
+
gap: 0.75rem;
|
|
5609
|
+
align-items: flex-start;
|
|
5610
|
+
max-width: 42rem;
|
|
5611
|
+
}
|
|
5612
|
+
|
|
5613
|
+
.c-message--user {
|
|
5614
|
+
align-self: flex-end;
|
|
5615
|
+
flex-direction: row-reverse;
|
|
5616
|
+
color: var(--c-text);
|
|
5617
|
+
}
|
|
5618
|
+
|
|
5619
|
+
.c-message--assistant {
|
|
5620
|
+
align-self: flex-start;
|
|
5621
|
+
color: var(--c-text);
|
|
5622
|
+
}
|
|
5623
|
+
|
|
5624
|
+
.c-message--system {
|
|
5625
|
+
align-self: center;
|
|
5626
|
+
font-size: 0.875rem;
|
|
5627
|
+
color: var(--c-muted);
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5630
|
+
.c-message__avatar {
|
|
5631
|
+
flex: none;
|
|
5632
|
+
}
|
|
5633
|
+
|
|
5634
|
+
.c-message__body {
|
|
5635
|
+
box-sizing: border-box;
|
|
5636
|
+
display: flex;
|
|
5637
|
+
flex: 0 1 auto;
|
|
5638
|
+
flex-direction: column;
|
|
5639
|
+
gap: 0.5rem;
|
|
5640
|
+
min-width: 0;
|
|
5641
|
+
padding: 0.75rem 1rem;
|
|
5642
|
+
border: 1px solid transparent;
|
|
5643
|
+
border-radius: 0;
|
|
5644
|
+
}
|
|
5645
|
+
|
|
5646
|
+
.c-message--user .c-message__body {
|
|
5647
|
+
background: var(--c-surface-active);
|
|
5648
|
+
border-color: var(--c-surface-active);
|
|
5649
|
+
}
|
|
5650
|
+
|
|
5651
|
+
.c-message--assistant .c-message__body {
|
|
5652
|
+
background: var(--c-surface);
|
|
5653
|
+
border-color: var(--c-surface-active);
|
|
5654
|
+
}
|
|
5655
|
+
|
|
5656
|
+
.c-message--system .c-message__body {
|
|
5657
|
+
background: transparent;
|
|
5658
|
+
border-color: transparent;
|
|
5659
|
+
}
|
|
5660
|
+
|
|
5661
|
+
.c-message__header {
|
|
5662
|
+
display: flex;
|
|
5663
|
+
flex-wrap: wrap;
|
|
5664
|
+
gap: 0.75rem;
|
|
5665
|
+
align-items: center;
|
|
5666
|
+
}
|
|
5667
|
+
|
|
5668
|
+
.c-message__name {
|
|
5669
|
+
display: block;
|
|
5670
|
+
font-size: 0.75rem;
|
|
5671
|
+
font-weight: 700;
|
|
5672
|
+
color: var(--c-muted);
|
|
5673
|
+
}
|
|
5674
|
+
|
|
5675
|
+
.c-message__content {
|
|
5676
|
+
min-width: 0;
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
.c-message__note {
|
|
5680
|
+
margin: 0;
|
|
5681
|
+
font-size: 0.875rem;
|
|
5682
|
+
}
|
|
5683
|
+
|
|
5684
|
+
.c-message__note--stopped {
|
|
5685
|
+
color: var(--c-muted);
|
|
5686
|
+
}
|
|
5687
|
+
|
|
5688
|
+
.c-message__note--failed {
|
|
5689
|
+
padding: 0.5rem 0.75rem;
|
|
5690
|
+
color: var(--c-error);
|
|
5691
|
+
background: var(--c-background);
|
|
5692
|
+
border: 1px solid var(--c-error);
|
|
5693
|
+
border-radius: 0;
|
|
5694
|
+
}
|
|
5695
|
+
|
|
5696
|
+
.c-message__actions {
|
|
5697
|
+
display: flex;
|
|
5698
|
+
flex-wrap: wrap;
|
|
5699
|
+
gap: 0.25rem;
|
|
5700
|
+
margin-top: 0.25rem;
|
|
5701
|
+
}
|
|
5702
|
+
|
|
5703
|
+
.c-message-list {
|
|
5704
|
+
position: relative;
|
|
5705
|
+
box-sizing: border-box;
|
|
5706
|
+
display: flex;
|
|
5707
|
+
flex-direction: column;
|
|
5708
|
+
height: 100%;
|
|
5709
|
+
min-height: 0;
|
|
5710
|
+
}
|
|
5711
|
+
|
|
5712
|
+
.c-message-list__log {
|
|
5713
|
+
box-sizing: border-box;
|
|
5714
|
+
flex: 1 1 auto;
|
|
5715
|
+
min-height: 0;
|
|
5716
|
+
padding: 1rem;
|
|
5717
|
+
overflow-y: auto;
|
|
5718
|
+
overscroll-behavior: contain;
|
|
5719
|
+
overflow-anchor: none;
|
|
5720
|
+
background: transparent;
|
|
5721
|
+
}
|
|
5722
|
+
.c-message-list__log:focus-visible {
|
|
5723
|
+
outline: 1px solid var(--c-focus);
|
|
5724
|
+
outline-offset: -2px;
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5727
|
+
.c-message-list__content {
|
|
5728
|
+
display: flex;
|
|
5729
|
+
flex-direction: column;
|
|
5730
|
+
gap: 1rem;
|
|
5731
|
+
}
|
|
5732
|
+
|
|
5733
|
+
.c-message-list__empty {
|
|
5734
|
+
padding: 2rem;
|
|
5735
|
+
}
|
|
5736
|
+
|
|
5737
|
+
.c-message-list__jump {
|
|
5738
|
+
position: absolute;
|
|
5739
|
+
right: 0;
|
|
5740
|
+
bottom: 1rem;
|
|
5741
|
+
left: 0;
|
|
5742
|
+
z-index: 1;
|
|
5743
|
+
display: flex;
|
|
5744
|
+
justify-content: center;
|
|
5745
|
+
pointer-events: none;
|
|
5746
|
+
}
|
|
5747
|
+
.c-message-list__jump > * {
|
|
5748
|
+
pointer-events: auto;
|
|
5749
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carissa-io/theme-default",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.16",
|
|
4
4
|
"description": "Carissa UI: the default theme (Swiss minimalism) as SCSS variable overrides of the Carissa components, with a compiled CSS build",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"style": "dist/index.css",
|
|
@@ -100,7 +100,12 @@
|
|
|
100
100
|
"@carissa-io/components-visually-hidden": "^0.1.0",
|
|
101
101
|
"@carissa-io/components-live-region": "^0.1.0",
|
|
102
102
|
"@carissa-io/components-theme-switch": "^0.1.0",
|
|
103
|
-
"@carissa-io/components-icon": "^0.1.0"
|
|
103
|
+
"@carissa-io/components-icon": "^0.1.0",
|
|
104
|
+
"@carissa-io/components-ai-label": "^0.1.0",
|
|
105
|
+
"@carissa-io/components-thinking-indicator": "^0.1.0",
|
|
106
|
+
"@carissa-io/components-streaming-text": "^0.1.0",
|
|
107
|
+
"@carissa-io/components-message": "^0.1.0",
|
|
108
|
+
"@carissa-io/components-message-list": "^0.1.0"
|
|
104
109
|
},
|
|
105
110
|
"peerDependenciesMeta": {
|
|
106
111
|
"@carissa-io/components-input": {
|
|
@@ -303,10 +308,26 @@
|
|
|
303
308
|
},
|
|
304
309
|
"@carissa-io/components-icon": {
|
|
305
310
|
"optional": true
|
|
311
|
+
},
|
|
312
|
+
"@carissa-io/components-ai-label": {
|
|
313
|
+
"optional": true
|
|
314
|
+
},
|
|
315
|
+
"@carissa-io/components-thinking-indicator": {
|
|
316
|
+
"optional": true
|
|
317
|
+
},
|
|
318
|
+
"@carissa-io/components-streaming-text": {
|
|
319
|
+
"optional": true
|
|
320
|
+
},
|
|
321
|
+
"@carissa-io/components-message": {
|
|
322
|
+
"optional": true
|
|
323
|
+
},
|
|
324
|
+
"@carissa-io/components-message-list": {
|
|
325
|
+
"optional": true
|
|
306
326
|
}
|
|
307
327
|
},
|
|
308
328
|
"devDependencies": {
|
|
309
329
|
"@carissa-io/components-accordion": "^0.1.0",
|
|
330
|
+
"@carissa-io/components-ai-label": "^0.1.0",
|
|
310
331
|
"@carissa-io/components-alert": "^0.1.0",
|
|
311
332
|
"@carissa-io/components-async-autocomplete": "^0.1.0",
|
|
312
333
|
"@carissa-io/components-avatar": "^0.1.0",
|
|
@@ -335,6 +356,8 @@
|
|
|
335
356
|
"@carissa-io/components-listbox": "^0.1.1",
|
|
336
357
|
"@carissa-io/components-live-region": "^0.1.0",
|
|
337
358
|
"@carissa-io/components-menu": "^0.1.0",
|
|
359
|
+
"@carissa-io/components-message": "^0.1.0",
|
|
360
|
+
"@carissa-io/components-message-list": "^0.1.0",
|
|
338
361
|
"@carissa-io/components-modal": "^0.1.2",
|
|
339
362
|
"@carissa-io/components-navbar": "^0.1.0",
|
|
340
363
|
"@carissa-io/components-number-input": "^0.1.0",
|
|
@@ -355,12 +378,14 @@
|
|
|
355
378
|
"@carissa-io/components-spinner": "^0.1.0",
|
|
356
379
|
"@carissa-io/components-stat": "^0.1.0",
|
|
357
380
|
"@carissa-io/components-stepper": "^0.1.0",
|
|
381
|
+
"@carissa-io/components-streaming-text": "^0.1.0",
|
|
358
382
|
"@carissa-io/components-switch": "^0.1.1",
|
|
359
383
|
"@carissa-io/components-table": "^0.1.0",
|
|
360
384
|
"@carissa-io/components-tabs": "^0.1.0",
|
|
361
385
|
"@carissa-io/components-tag": "^0.1.0",
|
|
362
386
|
"@carissa-io/components-tag-input": "^0.1.0",
|
|
363
387
|
"@carissa-io/components-theme-switch": "^0.1.0",
|
|
388
|
+
"@carissa-io/components-thinking-indicator": "^0.1.0",
|
|
364
389
|
"@carissa-io/components-time-picker": "^0.1.0",
|
|
365
390
|
"@carissa-io/components-timeline": "^0.1.0",
|
|
366
391
|
"@carissa-io/components-toast": "^0.1.0",
|
package/scss/_modes.scss
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Carissa Default theme color modes: the light and dark palettes, emitted as CSS custom properties (`--c-<role>`).
|
|
2
|
-
// Loading this partial emits the CSS below (`@use 'modes' as *` in index.scss)
|
|
2
|
+
// Loading this partial emits the CSS below (`@use 'modes' as *` in index.scss), including the two scrollbar rules (`:root` and `*`) at the end;
|
|
3
|
+
// `_tokens.scss` stays Sass-only.
|
|
3
4
|
//
|
|
4
5
|
// no attribute light (the default)
|
|
5
6
|
// data-mode="light" light
|
|
@@ -28,6 +29,8 @@ $light: (
|
|
|
28
29
|
'warning': t.$warning,
|
|
29
30
|
'info': t.$info,
|
|
30
31
|
'focus': t.$focus-color,
|
|
32
|
+
'scrollbar-thumb': t.$scrollbar-thumb,
|
|
33
|
+
'scrollbar-track': t.$scrollbar-track,
|
|
31
34
|
);
|
|
32
35
|
|
|
33
36
|
// Dark: near-black ground, the contrast of the light theme inverted (see DESIGN.md for the ratios).
|
|
@@ -48,6 +51,8 @@ $dark: (
|
|
|
48
51
|
'warning': #fbbf24,
|
|
49
52
|
'info': #60a5fa,
|
|
50
53
|
'focus': #fff,
|
|
54
|
+
'scrollbar-thumb': #737373,
|
|
55
|
+
'scrollbar-track': #0a0a0a, // the dark background
|
|
51
56
|
);
|
|
52
57
|
|
|
53
58
|
// What the component blocks read: `var(--c-<role>)`, one per role.
|
|
@@ -67,6 +72,8 @@ $var-success: var(--c-success);
|
|
|
67
72
|
$var-warning: var(--c-warning);
|
|
68
73
|
$var-info: var(--c-info);
|
|
69
74
|
$var-focus: var(--c-focus);
|
|
75
|
+
$var-scrollbar-thumb: var(--c-scrollbar-thumb);
|
|
76
|
+
$var-scrollbar-track: var(--c-scrollbar-track);
|
|
70
77
|
|
|
71
78
|
// The dropdown arrow of Select, Combobox and Date picker (the same roles as the `$arrow-*` tokens).
|
|
72
79
|
$var-arrow-color: $var-muted;
|
|
@@ -97,3 +104,16 @@ $var-arrow-color-disabled: $var-disabled-text;
|
|
|
97
104
|
@include palette($dark, dark);
|
|
98
105
|
}
|
|
99
106
|
}
|
|
107
|
+
|
|
108
|
+
// Scrollbars, in two rules because the two properties differ. `scrollbar-color` is inherited, so one declaration on `:root` reaches every
|
|
109
|
+
// scroll container. `scrollbar-width` is NOT inherited (only the page viewport would get it), so it is declared on every element with the
|
|
110
|
+
// universal selector. `*` has zero specificity (`:where(*)` would be the same; `*` is plain and just as supported), so it never beats a
|
|
111
|
+
// component rule: a scroller that sets its own width (the carousel hides its bar with `scrollbar-width: none`) keeps it, in any order.
|
|
112
|
+
// Standard properties only: colour and thickness, no rounding.
|
|
113
|
+
:root {
|
|
114
|
+
scrollbar-color: $var-scrollbar-thumb $var-scrollbar-track;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
* {
|
|
118
|
+
scrollbar-width: t.$scrollbar-width;
|
|
119
|
+
}
|
package/scss/_tokens.scss
CHANGED
|
@@ -21,6 +21,10 @@ $error: #dc3545;
|
|
|
21
21
|
$success: #15803d;
|
|
22
22
|
$warning: #a16207;
|
|
23
23
|
$info: #1d4ed8;
|
|
24
|
+
// Scrollbars (every scroll container inherits them from `:root`): a neutral thumb on a track that is the page ground, thin.
|
|
25
|
+
$scrollbar-thumb: #737373; // 4.74:1 on the white track and background (the neutral 500)
|
|
26
|
+
$scrollbar-track: $white; // the light background: the track is the page ground
|
|
27
|
+
$scrollbar-width: thin;
|
|
24
28
|
|
|
25
29
|
// Values shared by every component, so the whole theme moves together.
|
|
26
30
|
$font-family: ('Helvetica Neue', Helvetica, Arial, 'Liberation Sans', sans-serif);
|
package/scss/index.scss
CHANGED
|
@@ -1725,3 +1725,87 @@
|
|
|
1725
1725
|
$c-icon-size-lg: 1.5rem,
|
|
1726
1726
|
$c-icon-size-xl: 2rem
|
|
1727
1727
|
);
|
|
1728
|
+
|
|
1729
|
+
// AI label: the mark is a neutral Badge (configured above; the ai-label index forwards its styles, so this block comes after
|
|
1730
|
+
// the Badge one) and the optional details line is --c-muted text (5.74:1 / 7.85:1 on the background, 5.27:1 / 7.11:1 on
|
|
1731
|
+
// --c-surface) in the theme's caption and small sizes. It reads as text beside the mark: nothing opens.
|
|
1732
|
+
@use '@carissa-io/components-ai-label/scss' as ai-label with (
|
|
1733
|
+
$c-ai-label-gap: 0.5rem,
|
|
1734
|
+
$c-ai-label-details-color: $var-muted,
|
|
1735
|
+
$c-ai-label-details-font-size-sm: $font-size-caption,
|
|
1736
|
+
$c-ai-label-details-font-size-md: $font-size-small
|
|
1737
|
+
);
|
|
1738
|
+
|
|
1739
|
+
// Thinking indicator: the three dots are the text colour of the indicator, --c-muted (5.74:1 / 7.85:1 on the background; the
|
|
1740
|
+
// dots are decorative, the label carries the meaning), and they keep the component's own timing; the animation stops under
|
|
1741
|
+
// prefers-reduced-motion. The label is the theme's small body text in the same --c-muted.
|
|
1742
|
+
@use '@carissa-io/components-thinking-indicator/scss' as thinking-indicator with (
|
|
1743
|
+
$c-thinking-indicator-gap: 0.5rem,
|
|
1744
|
+
$c-thinking-indicator-color: $var-muted,
|
|
1745
|
+
$c-thinking-indicator-font-size: $font-size-small,
|
|
1746
|
+
$c-thinking-indicator-dot-color: currentColor
|
|
1747
|
+
);
|
|
1748
|
+
|
|
1749
|
+
// Streaming text: the answer is --c-text (17.93:1 / 18.16:1 on the background) at the theme's body line height. The caret is a
|
|
1750
|
+
// thin square bar in currentColor, so it always has the contrast of the text it follows, in both modes; it is steady under
|
|
1751
|
+
// prefers-reduced-motion. No other block forwards it.
|
|
1752
|
+
@use '@carissa-io/components-streaming-text/scss' as streaming-text with (
|
|
1753
|
+
$c-streaming-text-color: $var-text,
|
|
1754
|
+
$c-streaming-text-line-height: $line-height-normal,
|
|
1755
|
+
$c-streaming-text-caret-color: currentColor,
|
|
1756
|
+
$c-streaming-text-caret-width: 0.125em,
|
|
1757
|
+
$c-streaming-text-caret-radius: 0
|
|
1758
|
+
);
|
|
1759
|
+
|
|
1760
|
+
// Message: the roles differ by more than colour. The user's message sits at the end of the column on a --c-surface-active bubble
|
|
1761
|
+
// (--c-text 14.23:1 / 13.88:1), the assistant's at the start on --c-surface (16.44:1 / 16.44:1) and the system's is centred, muted
|
|
1762
|
+
// (--c-muted 5.74:1 / 7.85:1 on the background) and unframed; every message also shows its author name, a bold caption in --c-muted
|
|
1763
|
+
// (5.27:1 / 7.11:1 on --c-surface, 4.56:1 / 6.00:1 on --c-surface-active). Bubbles are square like the cards. A stopped note is
|
|
1764
|
+
// --c-muted text; a failed one is --c-error text in a 1px --c-error frame on --c-background (4.53:1 / 7.16:1), a sentence and a frame,
|
|
1765
|
+
// never colour alone. No other block forwards it.
|
|
1766
|
+
@use '@carissa-io/components-message/scss' as message with (
|
|
1767
|
+
$c-message-gap: 0.75rem,
|
|
1768
|
+
$c-message-max-width: 42rem,
|
|
1769
|
+
$c-message-padding-y: 0.75rem,
|
|
1770
|
+
$c-message-padding-x: 1rem,
|
|
1771
|
+
$c-message-radius: $radius,
|
|
1772
|
+
$c-message-border-width: 1px,
|
|
1773
|
+
$c-message-body-gap: 0.5rem,
|
|
1774
|
+
$c-message-user-color: $var-text,
|
|
1775
|
+
$c-message-user-background: $var-surface-active,
|
|
1776
|
+
$c-message-user-border-color: $var-surface-active,
|
|
1777
|
+
$c-message-assistant-color: $var-text,
|
|
1778
|
+
$c-message-assistant-background: $var-surface,
|
|
1779
|
+
$c-message-assistant-border-color: $var-surface-active,
|
|
1780
|
+
$c-message-system-color: $var-muted,
|
|
1781
|
+
$c-message-system-background: transparent,
|
|
1782
|
+
$c-message-system-border-color: transparent,
|
|
1783
|
+
$c-message-system-font-size: $font-size-small,
|
|
1784
|
+
$c-message-name-color: $var-muted,
|
|
1785
|
+
$c-message-name-font-size: $font-size-caption,
|
|
1786
|
+
$c-message-name-font-weight: $font-weight-bold,
|
|
1787
|
+
$c-message-header-gap: 0.75rem,
|
|
1788
|
+
$c-message-note-font-size: $font-size-small,
|
|
1789
|
+
$c-message-stopped-color: $var-muted,
|
|
1790
|
+
$c-message-failed-color: $var-error,
|
|
1791
|
+
$c-message-failed-background: $var-background,
|
|
1792
|
+
$c-message-failed-border-color: $var-error,
|
|
1793
|
+
$c-message-failed-radius: $radius,
|
|
1794
|
+
$c-message-failed-padding: 0.5rem 0.75rem,
|
|
1795
|
+
$c-message-actions-gap: 0.25rem,
|
|
1796
|
+
$c-message-actions-margin-top: 0.25rem
|
|
1797
|
+
);
|
|
1798
|
+
|
|
1799
|
+
// Message list: no fill of its own (it sits on the page background, so the bubbles above carry the contrast). The log takes
|
|
1800
|
+
// keyboard focus, so it gets the theme's 1px --c-focus ring drawn inside (21:1 / 19.8:1). The "Jump to latest" button is the
|
|
1801
|
+
// secondary Button (configured above; the list index forwards its styles, so this block comes after the Button, Live region
|
|
1802
|
+
// and Icon ones) floating 1rem above the bottom edge, centred. The empty state is the EmptyState's (configured above).
|
|
1803
|
+
@use '@carissa-io/components-message-list/scss' as message-list with (
|
|
1804
|
+
$c-message-list-padding: 1rem,
|
|
1805
|
+
$c-message-list-gap: 1rem,
|
|
1806
|
+
$c-message-list-focus-outline: $focus-width solid $var-focus,
|
|
1807
|
+
$c-message-list-focus-outline-offset: -2px,
|
|
1808
|
+
$c-message-list-empty-padding: 2rem,
|
|
1809
|
+
$c-message-list-jump-bottom: 1rem,
|
|
1810
|
+
$c-message-list-jump-z-index: 1
|
|
1811
|
+
);
|