@stisla/style 3.0.0-beta.8
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/LICENSE +9 -0
- package/README.md +16 -0
- package/dist/accordion/accordion.css +194 -0
- package/dist/alert/alert.css +138 -0
- package/dist/autocomplete/autocomplete.css +193 -0
- package/dist/avatar/avatar.css +142 -0
- package/dist/avatar-group/avatar-group.css +42 -0
- package/dist/badge/badge.css +74 -0
- package/dist/breadcrumb/breadcrumb.css +71 -0
- package/dist/button/button.css +318 -0
- package/dist/button/index.d.ts +1 -0
- package/dist/button/index.js +6 -0
- package/dist/button-group/button-group.css +108 -0
- package/dist/card/card.css +219 -0
- package/dist/carousel/carousel.css +170 -0
- package/dist/checkbox/checkbox.css +98 -0
- package/dist/chunk-K45KLI3Y.js +74 -0
- package/dist/collapsible/collapsible.css +36 -0
- package/dist/combobox/combobox.css +106 -0
- package/dist/combobox/combobox.tomselect.css +251 -0
- package/dist/config-CARtrJ7I.d.ts +61 -0
- package/dist/dialog/dialog.css +258 -0
- package/dist/drawer/drawer.css +318 -0
- package/dist/empty-state/empty-state.css +138 -0
- package/dist/field/field.css +70 -0
- package/dist/icon-box/icon-box.css +64 -0
- package/dist/illustration/illustration.css +103 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +60 -0
- package/dist/indicator/indicator.css +84 -0
- package/dist/input/input.css +220 -0
- package/dist/input-group/input-group.css +141 -0
- package/dist/kbd/kbd.css +55 -0
- package/dist/link/link.css +28 -0
- package/dist/list-group/list-group.css +261 -0
- package/dist/media/media.css +115 -0
- package/dist/menu/menu.css +237 -0
- package/dist/meter/meter.css +124 -0
- package/dist/navbar/navbar.css +170 -0
- package/dist/page/page.css +95 -0
- package/dist/pagination/pagination.css +125 -0
- package/dist/placeholders/placeholders.css +58 -0
- package/dist/popover/popover.css +251 -0
- package/dist/progress/progress.css +139 -0
- package/dist/radio/radio.css +81 -0
- package/dist/scroll-area/scroll-area.css +25 -0
- package/dist/scroll-area/scroll-area.overlayscrollbars.css +42 -0
- package/dist/select/select.css +282 -0
- package/dist/separator/separator.css +26 -0
- package/dist/sidebar/sidebar.css +493 -0
- package/dist/slider/slider.css +159 -0
- package/dist/spinner/spinner.css +65 -0
- package/dist/switch/switch.css +91 -0
- package/dist/table/table.css +284 -0
- package/dist/tabs/tabs.css +137 -0
- package/dist/textarea/textarea.css +99 -0
- package/dist/timeline/timeline.css +271 -0
- package/dist/toast/toast.css +267 -0
- package/dist/toggle/toggle.css +125 -0
- package/dist/toggle-group/toggle-group.css +87 -0
- package/dist/tooltip/tooltip.css +95 -0
- package/package.json +46 -0
- package/src/theme.css +151 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { C as ComposerProps, a as Composed } from './config-CARtrJ7I.js';
|
|
2
|
+
export { b as ComposerConfig, T as Tune, V as VariantDefs, c as button, d as composer } from './config-CARtrJ7I.js';
|
|
3
|
+
|
|
4
|
+
declare const sidebarKnobs: readonly ["gap", "width", "widthCollapsed", "bg", "color", "paddingBlock", "paddingInline", "transitionDuration", "brandColor", "brandIconSize", "brandGap", "groupGap", "groupTitleColor", "groupTitleFontSize", "groupTitleFontWeight", "buttonGap", "buttonHeight", "buttonPaddingInline", "buttonRadius", "buttonColor", "buttonFontWeight", "buttonIconSize", "buttonIconColor", "buttonBgHover", "buttonColorHover", "buttonBgActive", "buttonColorActive", "itemActionSize", "submenuBorderColor", "submenuPaddingInlineStart", "submenuMarginInlineStart"];
|
|
5
|
+
type SidebarKnob = (typeof sidebarKnobs)[number];
|
|
6
|
+
declare const sidebar: (props?: ComposerProps<{
|
|
7
|
+
size: {
|
|
8
|
+
sm: string;
|
|
9
|
+
md: string;
|
|
10
|
+
lg: string;
|
|
11
|
+
};
|
|
12
|
+
}, "gap" | "paddingInline" | "color" | "bg" | "width" | "widthCollapsed" | "paddingBlock" | "transitionDuration" | "brandColor" | "brandIconSize" | "brandGap" | "groupGap" | "groupTitleColor" | "groupTitleFontSize" | "groupTitleFontWeight" | "buttonGap" | "buttonHeight" | "buttonPaddingInline" | "buttonRadius" | "buttonColor" | "buttonFontWeight" | "buttonIconSize" | "buttonIconColor" | "buttonBgHover" | "buttonColorHover" | "buttonBgActive" | "buttonColorActive" | "itemActionSize" | "submenuBorderColor" | "submenuPaddingInlineStart" | "submenuMarginInlineStart"> | undefined) => Composed;
|
|
13
|
+
|
|
14
|
+
export { Composed, ComposerProps, type SidebarKnob, sidebar, sidebarKnobs };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
button,
|
|
3
|
+
composer
|
|
4
|
+
} from "./chunk-K45KLI3Y.js";
|
|
5
|
+
|
|
6
|
+
// src/sidebar/config.ts
|
|
7
|
+
var sidebarKnobs = [
|
|
8
|
+
// Shell
|
|
9
|
+
"gap",
|
|
10
|
+
"width",
|
|
11
|
+
"widthCollapsed",
|
|
12
|
+
"bg",
|
|
13
|
+
"color",
|
|
14
|
+
"paddingBlock",
|
|
15
|
+
"paddingInline",
|
|
16
|
+
"transitionDuration",
|
|
17
|
+
// Brand
|
|
18
|
+
"brandColor",
|
|
19
|
+
"brandIconSize",
|
|
20
|
+
"brandGap",
|
|
21
|
+
// Group
|
|
22
|
+
"groupGap",
|
|
23
|
+
"groupTitleColor",
|
|
24
|
+
"groupTitleFontSize",
|
|
25
|
+
"groupTitleFontWeight",
|
|
26
|
+
// Button (item)
|
|
27
|
+
"buttonGap",
|
|
28
|
+
"buttonHeight",
|
|
29
|
+
"buttonPaddingInline",
|
|
30
|
+
"buttonRadius",
|
|
31
|
+
"buttonColor",
|
|
32
|
+
"buttonFontWeight",
|
|
33
|
+
"buttonIconSize",
|
|
34
|
+
"buttonIconColor",
|
|
35
|
+
"buttonBgHover",
|
|
36
|
+
"buttonColorHover",
|
|
37
|
+
"buttonBgActive",
|
|
38
|
+
"buttonColorActive",
|
|
39
|
+
// Item action
|
|
40
|
+
"itemActionSize",
|
|
41
|
+
// Submenu
|
|
42
|
+
"submenuBorderColor",
|
|
43
|
+
"submenuPaddingInlineStart",
|
|
44
|
+
"submenuMarginInlineStart"
|
|
45
|
+
];
|
|
46
|
+
var sidebar = composer({
|
|
47
|
+
base: "sidebar",
|
|
48
|
+
variants: {
|
|
49
|
+
size: { sm: "sidebar--sm", md: "", lg: "sidebar--lg" }
|
|
50
|
+
},
|
|
51
|
+
defaultVariants: { size: "md" },
|
|
52
|
+
knobPrefix: "sidebar",
|
|
53
|
+
knobs: sidebarKnobs
|
|
54
|
+
});
|
|
55
|
+
export {
|
|
56
|
+
button,
|
|
57
|
+
composer,
|
|
58
|
+
sidebar,
|
|
59
|
+
sidebarKnobs
|
|
60
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* @stisla/style — Indicator. Ported from src/scss/components/_indicator.scss. A contentless status dot
|
|
2
|
+
* (presence, liveness, unread) — distinct from .badge, which labels with a count or word. The dot is
|
|
3
|
+
* the ::after; an opt-in pulse halo is the ::before. Pin it over an avatar/icon corner with absolute
|
|
4
|
+
* positioning on a relative host. References the @theme tokens (colors var(--color-*), spacing/sizes
|
|
5
|
+
* --spacing(n)). Knobs are --indicator-*. Sizes compact/roomy → sm/lg. @layer components.
|
|
6
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
7
|
+
|
|
8
|
+
@layer components {
|
|
9
|
+
.indicator {
|
|
10
|
+
position: relative;
|
|
11
|
+
display: inline-block;
|
|
12
|
+
flex-shrink: 0;
|
|
13
|
+
width: var(--indicator-size, --spacing(2.5));
|
|
14
|
+
height: var(--indicator-size, --spacing(2.5));
|
|
15
|
+
vertical-align: middle;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* The dot — ::after so it paints above the ::before pulse without a z-index. The ring is an opt-in
|
|
19
|
+
separator halo (0-width by default) for sitting the dot over a busy surface. */
|
|
20
|
+
.indicator::after {
|
|
21
|
+
content: "";
|
|
22
|
+
position: absolute;
|
|
23
|
+
inset: 0;
|
|
24
|
+
background: var(--indicator-color, var(--color-muted-foreground));
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
box-shadow: 0 0 0 var(--indicator-ring-width, 0px) var(--indicator-ring-color, var(--color-background));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* === Intent modifiers === */
|
|
30
|
+
.indicator--primary {
|
|
31
|
+
--indicator-color: var(--color-primary);
|
|
32
|
+
}
|
|
33
|
+
.indicator--success {
|
|
34
|
+
--indicator-color: var(--color-success);
|
|
35
|
+
}
|
|
36
|
+
.indicator--warning {
|
|
37
|
+
--indicator-color: var(--color-warning);
|
|
38
|
+
}
|
|
39
|
+
.indicator--danger {
|
|
40
|
+
--indicator-color: var(--color-danger);
|
|
41
|
+
}
|
|
42
|
+
.indicator--info {
|
|
43
|
+
--indicator-color: var(--color-info);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* === Sizes (base = md) === */
|
|
47
|
+
.indicator--sm {
|
|
48
|
+
--indicator-size: --spacing(2);
|
|
49
|
+
}
|
|
50
|
+
.indicator--lg {
|
|
51
|
+
--indicator-size: --spacing(3.5);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* === Pulse (ping halo) === an expanding, fading ring in the dot's color for "live" signals. The
|
|
55
|
+
::before paints behind the ::after dot and emanates outward. */
|
|
56
|
+
.indicator--pulse::before {
|
|
57
|
+
content: "";
|
|
58
|
+
position: absolute;
|
|
59
|
+
inset: 0;
|
|
60
|
+
background: var(--indicator-color, var(--color-muted-foreground));
|
|
61
|
+
border-radius: 50%;
|
|
62
|
+
opacity: 0;
|
|
63
|
+
animation: st-indicator-pulse var(--indicator-pulse-duration, 1.2s) cubic-bezier(0, 0, 0.2, 1) infinite;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* A still dot reads as a valid status (unlike a frozen spinner), so drop the ping rather than slow it. */
|
|
67
|
+
@media (prefers-reduced-motion: reduce) {
|
|
68
|
+
.indicator--pulse::before {
|
|
69
|
+
animation: none;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@keyframes st-indicator-pulse {
|
|
75
|
+
0% {
|
|
76
|
+
transform: scale(1);
|
|
77
|
+
opacity: 0.55;
|
|
78
|
+
}
|
|
79
|
+
75%,
|
|
80
|
+
100% {
|
|
81
|
+
transform: scale(2.5);
|
|
82
|
+
opacity: 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/* @stisla/style — Input. Ported from src/scss/components/_input.scss + the form-field-base mixin
|
|
2
|
+
* (foundation/_mixins.scss). References the @theme tokens: colors var(--color-*), spacing
|
|
3
|
+
* --spacing(n), type var(--text-*) / var(--font-weight-*), radius var(--radius-*). Only no-namespace
|
|
4
|
+
* customs use --st-* (border-width, duration). Knobs are --input-* (fallback-default). @layer components.
|
|
5
|
+
*
|
|
6
|
+
* The shared field base (border, height, padding, focus halo, validation, hover, disabled, readonly)
|
|
7
|
+
* is DUPLICATED here with the --input-* namespace — plain CSS has no parameterized mixin. Keep the
|
|
8
|
+
* base in sync with textarea.css + select.css (the other two consumers of the former form-field-base).
|
|
9
|
+
* Authoring rules: ../../../../PORTING.md */
|
|
10
|
+
|
|
11
|
+
@layer components {
|
|
12
|
+
/* === Shared field base (= former form-field-base, --input-* namespace) === */
|
|
13
|
+
.input {
|
|
14
|
+
display: block;
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: var(--input-height, --spacing(9));
|
|
17
|
+
padding-block: var(--input-padding-block, 0);
|
|
18
|
+
padding-inline: var(--input-padding-inline, --spacing(2.5));
|
|
19
|
+
font-family: inherit;
|
|
20
|
+
font-size: var(--input-font-size, var(--text-sm));
|
|
21
|
+
font-weight: var(--font-weight-normal);
|
|
22
|
+
line-height: var(--leading-tight);
|
|
23
|
+
color: var(--input-color, var(--color-foreground));
|
|
24
|
+
background: var(--input-bg, var(--color-surface));
|
|
25
|
+
border: var(--input-border-width, var(--st-border-width)) solid
|
|
26
|
+
var(--input-border-color, var(--color-border-strong));
|
|
27
|
+
border-radius: var(--input-radius, var(--radius-md));
|
|
28
|
+
/* Strip UA chrome (search cancel, number spinner); types that want an indicator paint it back. */
|
|
29
|
+
appearance: none;
|
|
30
|
+
transition:
|
|
31
|
+
border-color var(--transition-duration-fast) ease,
|
|
32
|
+
box-shadow var(--transition-duration-fast) ease,
|
|
33
|
+
background-color var(--transition-duration-fast) ease;
|
|
34
|
+
|
|
35
|
+
/* iOS Safari zooms when a focused field's font-size < 16px and never zooms back. Bump touch to 1rem. */
|
|
36
|
+
@media (pointer: coarse) {
|
|
37
|
+
font-size: var(--input-font-size, var(--text-base));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&::placeholder {
|
|
41
|
+
color: var(--input-placeholder, var(--color-muted-foreground));
|
|
42
|
+
opacity: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Hover — only when idle. Focus owns the ring; invalid owns the red rim; disabled rejects the cue. */
|
|
46
|
+
&:hover:not(
|
|
47
|
+
:disabled,
|
|
48
|
+
:focus-visible,
|
|
49
|
+
[aria-invalid="true"],
|
|
50
|
+
:user-invalid
|
|
51
|
+
) {
|
|
52
|
+
border-color: color-mix(
|
|
53
|
+
in oklch,
|
|
54
|
+
var(--color-border-strong) 80%,
|
|
55
|
+
var(--color-foreground)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Focus — soft halo + primary border (the field already wears a border; an offset outline reads
|
|
60
|
+
aggressive against a continuously-bordered shape). */
|
|
61
|
+
&:focus-visible {
|
|
62
|
+
outline: none;
|
|
63
|
+
border-color: var(--color-primary);
|
|
64
|
+
box-shadow: 0 0 0 3px
|
|
65
|
+
color-mix(in oklch, var(--color-ring) 25%, transparent);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Validation. aria-invalid for server / JS / explicit; :user-invalid for native validation. */
|
|
69
|
+
&[aria-invalid="true"],
|
|
70
|
+
&:user-invalid {
|
|
71
|
+
border-color: var(--color-danger);
|
|
72
|
+
}
|
|
73
|
+
&[aria-invalid="true"]:focus-visible,
|
|
74
|
+
&:user-invalid:focus-visible {
|
|
75
|
+
border-color: var(--color-danger);
|
|
76
|
+
box-shadow: 0 0 0 3px
|
|
77
|
+
color-mix(in oklch, var(--color-danger) 25%, transparent);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&:disabled {
|
|
81
|
+
opacity: 0.55;
|
|
82
|
+
cursor: not-allowed;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Readonly (not disabled) — swap bg one tier so it reads "selectable, not editable". */
|
|
86
|
+
&[readonly]:not(:disabled) {
|
|
87
|
+
background: var(--color-surface-2);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@media (prefers-reduced-motion: reduce) {
|
|
91
|
+
transition: none;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* === <input type="file"> ===
|
|
96
|
+
* Flex row: the selector button is a small inset chip (mirrors .button--sm.button--neutral; the
|
|
97
|
+
* shadow DOM blocks .button classes, so the rules are inlined) and the filename trails after it. */
|
|
98
|
+
.input[type="file"] {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
color: var(--color-muted-foreground);
|
|
103
|
+
}
|
|
104
|
+
.input[type="file"]::file-selector-button {
|
|
105
|
+
margin-block-start: 0.4063rem;
|
|
106
|
+
margin-inline-end: --spacing(2.5);
|
|
107
|
+
padding: --spacing(0.5) --spacing(2);
|
|
108
|
+
font-family: inherit;
|
|
109
|
+
font-size: var(--text-xs);
|
|
110
|
+
font-weight: var(--font-weight-medium);
|
|
111
|
+
line-height: var(--leading-tight);
|
|
112
|
+
color: var(--color-neutral-foreground);
|
|
113
|
+
background: var(--color-neutral);
|
|
114
|
+
border: var(--st-border-width) solid
|
|
115
|
+
color-mix(
|
|
116
|
+
in oklch,
|
|
117
|
+
var(--color-neutral) 85%,
|
|
118
|
+
var(--color-muted-foreground)
|
|
119
|
+
);
|
|
120
|
+
border-radius: calc(var(--input-radius, var(--radius-md)) - --spacing(1));
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
transition: background-color var(--transition-duration-fast) ease;
|
|
123
|
+
}
|
|
124
|
+
.input[type="file"]::file-selector-button:hover {
|
|
125
|
+
background: color-mix(in oklch, var(--color-neutral) 88%, black);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* === Native picker indicators ===
|
|
129
|
+
* The UA paints ::-webkit-calendar-picker-indicator black in both themes (invisible on a dark
|
|
130
|
+
* surface). Replace with a lucide glyph painted via background-color + mask so it tracks
|
|
131
|
+
* --color-muted-foreground and themes for free. Firefox has no such pseudo (fully native picker). */
|
|
132
|
+
.input[type="date"]::-webkit-calendar-picker-indicator,
|
|
133
|
+
.input[type="datetime-local"]::-webkit-calendar-picker-indicator,
|
|
134
|
+
.input[type="month"]::-webkit-calendar-picker-indicator,
|
|
135
|
+
.input[type="week"]::-webkit-calendar-picker-indicator {
|
|
136
|
+
width: --spacing(4);
|
|
137
|
+
height: --spacing(4);
|
|
138
|
+
background-color: var(--color-muted-foreground);
|
|
139
|
+
background-image: none;
|
|
140
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v4'/%3E%3Cpath d='M16 2v4'/%3E%3Crect width='18' height='18' x='3' y='4' rx='2'/%3E%3Cpath d='M3 10h18'/%3E%3C/svg%3E")
|
|
141
|
+
no-repeat center / contain;
|
|
142
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 2v4'/%3E%3Cpath d='M16 2v4'/%3E%3Crect width='18' height='18' x='3' y='4' rx='2'/%3E%3Cpath d='M3 10h18'/%3E%3C/svg%3E")
|
|
143
|
+
no-repeat center / contain;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
opacity: 1;
|
|
146
|
+
transition: background-color var(--transition-duration-fast) ease;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.input[type="time"]::-webkit-calendar-picker-indicator {
|
|
150
|
+
width: --spacing(4);
|
|
151
|
+
height: --spacing(4);
|
|
152
|
+
background-color: var(--color-muted-foreground);
|
|
153
|
+
background-image: none;
|
|
154
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E")
|
|
155
|
+
no-repeat center / contain;
|
|
156
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E")
|
|
157
|
+
no-repeat center / contain;
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
opacity: 1;
|
|
160
|
+
transition: background-color var(--transition-duration-fast) ease;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.input::-webkit-calendar-picker-indicator:hover {
|
|
164
|
+
background-color: var(--color-foreground);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* === <input type="color"> ===
|
|
168
|
+
* Native swatch inside our field shape. Width collapses to a small fixed swatch; inner padding lets
|
|
169
|
+
* the swatch wear the field radius via the inner-radius rule (outer − outer-padding). */
|
|
170
|
+
.input[type="color"] {
|
|
171
|
+
width: --spacing(12);
|
|
172
|
+
padding: --spacing(1);
|
|
173
|
+
cursor: pointer;
|
|
174
|
+
}
|
|
175
|
+
.input[type="color"]::-webkit-color-swatch {
|
|
176
|
+
border: 0;
|
|
177
|
+
border-radius: calc(var(--input-radius, var(--radius-md)) - --spacing(1));
|
|
178
|
+
}
|
|
179
|
+
.input[type="color"]::-moz-color-swatch {
|
|
180
|
+
border: 0;
|
|
181
|
+
border-radius: calc(var(--input-radius, var(--radius-md)) - --spacing(1));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* === Sizes (base = md) === */
|
|
185
|
+
.input--sm {
|
|
186
|
+
--input-radius: var(--radius-sm);
|
|
187
|
+
--input-height: --spacing(7);
|
|
188
|
+
--input-padding-inline: --spacing(2);
|
|
189
|
+
--input-font-size: var(--text-xs);
|
|
190
|
+
@media (pointer: coarse) {
|
|
191
|
+
--input-font-size: var(--text-base);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.input--lg {
|
|
196
|
+
--input-radius: var(--radius-lg);
|
|
197
|
+
--input-height: --spacing(11);
|
|
198
|
+
--input-padding-inline: --spacing(3.5);
|
|
199
|
+
--input-font-size: var(--text-base);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* === Plaintext ===
|
|
203
|
+
* Readonly bare-text variant. Strips the field affordance but keeps the height so it aligns with
|
|
204
|
+
* sibling .input fields in a form row. Pair with `readonly` on the input. */
|
|
205
|
+
.input--plaintext {
|
|
206
|
+
display: inline-flex;
|
|
207
|
+
align-items: center;
|
|
208
|
+
width: 100%;
|
|
209
|
+
height: --spacing(9);
|
|
210
|
+
padding: 0;
|
|
211
|
+
font-family: inherit;
|
|
212
|
+
font-size: var(--text-sm);
|
|
213
|
+
line-height: var(--leading-tight);
|
|
214
|
+
color: var(--color-foreground);
|
|
215
|
+
background: transparent;
|
|
216
|
+
border: var(--st-border-width) solid transparent;
|
|
217
|
+
outline: none;
|
|
218
|
+
appearance: none;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* @stisla/style — Input group. Ported from src/scss/components/_input-group.scss. Wrapper-owns-chrome:
|
|
2
|
+
* the .input-group carries the border, bg, radius, and focus halo; field children go transparent so
|
|
3
|
+
* the addon + input pair shares one continuous surface with one focus state. References the @theme
|
|
4
|
+
* tokens (colors var(--color-*), spacing --spacing(n), radius var(--radius-*)); only no-namespace
|
|
5
|
+
* customs use --st-* (border-width, duration). Knobs are --input-group-*. State via attributes
|
|
6
|
+
* (:focus-visible / [aria-invalid] / :user-invalid / :disabled forwarded through :has()), no is-*.
|
|
7
|
+
* @layer components. Authoring rules: ../../../../PORTING.md */
|
|
8
|
+
|
|
9
|
+
@layer components {
|
|
10
|
+
.input-group {
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: stretch;
|
|
13
|
+
width: 100%;
|
|
14
|
+
/* Border-inclusive height (border-box): the wrapper owns the control height; its border eats INTO
|
|
15
|
+
this value. Inner fields drop their own height and stretch to fill. */
|
|
16
|
+
height: var(--input-group-height, --spacing(9));
|
|
17
|
+
background: var(--input-group-bg, var(--color-surface));
|
|
18
|
+
border: var(--input-group-border-width, var(--st-border-width)) solid
|
|
19
|
+
var(--input-group-border-color, var(--color-border-strong));
|
|
20
|
+
border-radius: var(--input-group-radius, var(--radius-md));
|
|
21
|
+
transition:
|
|
22
|
+
border-color var(--transition-duration-fast) ease,
|
|
23
|
+
box-shadow var(--transition-duration-fast) ease;
|
|
24
|
+
|
|
25
|
+
/* Focus the whole group when an editable child is focused. */
|
|
26
|
+
&:has(> :is(.input, .select, .textarea):focus-visible) {
|
|
27
|
+
border-color: var(--color-primary);
|
|
28
|
+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-ring) 25%, transparent);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Validation forwarding — an invalid child paints the wrapper red so the message reads on one row. */
|
|
32
|
+
&:has(> [aria-invalid="true"]),
|
|
33
|
+
&:has(> :user-invalid) {
|
|
34
|
+
border-color: var(--color-danger);
|
|
35
|
+
}
|
|
36
|
+
&:has(> [aria-invalid="true"]:focus-visible),
|
|
37
|
+
&:has(> :user-invalid:focus-visible) {
|
|
38
|
+
box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-danger) 25%, transparent);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Strip chrome from field children — wrapper owns it. Zero only background-color (not the
|
|
42
|
+
`background` shorthand) so a <select>'s chevron image survives. Reset hover + focus too so the
|
|
43
|
+
field's own halo doesn't fight the group focus. */
|
|
44
|
+
> :is(.input, .select, .textarea) {
|
|
45
|
+
background-color: transparent;
|
|
46
|
+
border-color: transparent;
|
|
47
|
+
box-shadow: none;
|
|
48
|
+
height: auto;
|
|
49
|
+
|
|
50
|
+
&:hover,
|
|
51
|
+
&:focus-visible {
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
border-color: transparent;
|
|
54
|
+
box-shadow: none;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Textarea groups grow with content — release the fixed wrapper height. */
|
|
59
|
+
&:has(> .textarea) {
|
|
60
|
+
height: auto;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
> :is(.input, .select, .textarea)[readonly] {
|
|
64
|
+
background-color: transparent;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Inset buttons — a chip floating inside the field, not a slab welded to its edge. Height
|
|
68
|
+
re-derives from the wrapper (minus its border and 2× inset) so the chip centers with even
|
|
69
|
+
gaps; inner radius stays concentric with the wrapper. */
|
|
70
|
+
> .button {
|
|
71
|
+
height: calc(
|
|
72
|
+
var(--input-group-height, --spacing(9)) - 2 * var(--st-border-width) - 2 *
|
|
73
|
+
var(--input-group-inset, --spacing(1))
|
|
74
|
+
);
|
|
75
|
+
margin: var(--input-group-inset, --spacing(1));
|
|
76
|
+
border-radius: calc(
|
|
77
|
+
var(--input-group-radius, var(--radius-md)) - var(--input-group-inset, --spacing(1))
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Drop the addon's inner padding where it meets a sibling field (no seam → matching padding would
|
|
82
|
+
double into a too-wide gap). Keep the outside padding so the addon hugs the wrapper edge. */
|
|
83
|
+
> .input-group__text:first-child {
|
|
84
|
+
padding-inline-end: 0;
|
|
85
|
+
}
|
|
86
|
+
> .input-group__text:last-child {
|
|
87
|
+
padding-inline-start: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Textareas grow past the height floor; anchor addons to the first line. */
|
|
91
|
+
&:has(> .textarea) > .input-group__text {
|
|
92
|
+
align-self: flex-start;
|
|
93
|
+
height: var(--input-group-height, --spacing(9));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* Disabled forwarding — the transparent inner field can't show its own dim, so move it to the
|
|
97
|
+
wrapper and reset the child's opacity so the dim doesn't compound. */
|
|
98
|
+
&:has(> :disabled) {
|
|
99
|
+
opacity: 0.55;
|
|
100
|
+
> :is(.input, .select, .textarea):disabled {
|
|
101
|
+
opacity: 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@media (prefers-reduced-motion: reduce) {
|
|
106
|
+
transition: none;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* === Sizes (base = md) — pair with the field child's own size modifier (.input--sm, etc.). === */
|
|
111
|
+
.input-group--sm {
|
|
112
|
+
--input-group-radius: var(--radius-sm);
|
|
113
|
+
--input-group-height: --spacing(7);
|
|
114
|
+
--input-group-padding-inline: --spacing(2);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.input-group--lg {
|
|
118
|
+
--input-group-radius: var(--radius-lg);
|
|
119
|
+
--input-group-height: --spacing(11);
|
|
120
|
+
--input-group-padding-inline: --spacing(3.5);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* === Addon === no bg, no border, muted color; inline-flex centers icon + label across the height. */
|
|
124
|
+
.input-group__text {
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
padding-inline: var(--input-group-padding-inline, --spacing(2.5));
|
|
128
|
+
background: transparent;
|
|
129
|
+
border: 0;
|
|
130
|
+
color: var(--input-group-addon-color, var(--color-muted-foreground));
|
|
131
|
+
font-size: inherit;
|
|
132
|
+
line-height: 1.2;
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
|
|
135
|
+
> :is(svg, i) {
|
|
136
|
+
width: --spacing(4);
|
|
137
|
+
height: --spacing(4);
|
|
138
|
+
flex-shrink: 0;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/dist/kbd/kbd.css
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* @stisla/style — Kbd. Ported from src/scss/components/_kbd.scss. A keyboard-shortcut chip. <kbd> is
|
|
2
|
+
* unstyled at the bare-element level (reboot); .kbd opts in. Sits in the neutral interactional tier;
|
|
3
|
+
* the rim uses the same color-mix recipe as .button--neutral so the border stays visible in dark mode.
|
|
4
|
+
* Font-size is rem-based (a deliberate UI element, not tracking surrounding text). References the
|
|
5
|
+
* @theme tokens (colors var(--color-*), spacing --spacing(n), type var(--text-*) / var(--font-mono) /
|
|
6
|
+
* var(--font-weight-*) / var(--leading-*), radius var(--radius-*)); only no-namespace customs use
|
|
7
|
+
* --st-* (border-width). Knobs are --kbd-*. @layer components. Authoring rules: ../../../../PORTING.md */
|
|
8
|
+
|
|
9
|
+
@layer components {
|
|
10
|
+
.kbd {
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
gap: var(--kbd-gap, --spacing(1));
|
|
14
|
+
padding-block: var(--kbd-padding-block, --spacing(0.75));
|
|
15
|
+
padding-inline: var(--kbd-padding-inline, --spacing(2));
|
|
16
|
+
font-family: var(--font-mono);
|
|
17
|
+
font-size: var(--kbd-font-size, var(--text-xs));
|
|
18
|
+
font-weight: var(--kbd-font-weight, var(--font-weight-medium));
|
|
19
|
+
line-height: var(--leading-none);
|
|
20
|
+
color: var(--kbd-color, var(--color-neutral-foreground));
|
|
21
|
+
background: var(--kbd-bg, var(--color-neutral));
|
|
22
|
+
border: var(--st-border-width) solid
|
|
23
|
+
var(
|
|
24
|
+
--kbd-rim,
|
|
25
|
+
color-mix(
|
|
26
|
+
in oklch,
|
|
27
|
+
var(--color-neutral) 85%,
|
|
28
|
+
var(--color-muted-foreground)
|
|
29
|
+
)
|
|
30
|
+
);
|
|
31
|
+
border-radius: var(--kbd-radius, 0.5rem); /* intentional for 8px */
|
|
32
|
+
/* Bottom edge in the rim color so the chip reads as a key cap. */
|
|
33
|
+
box-shadow: 0 1px 0
|
|
34
|
+
var(
|
|
35
|
+
--kbd-rim,
|
|
36
|
+
color-mix(
|
|
37
|
+
in oklch,
|
|
38
|
+
var(--color-neutral) 85%,
|
|
39
|
+
var(--color-muted-foreground)
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Combo shortcuts — a .kbd wrapping nested .kbd elements becomes an invisible wrapper so each inner
|
|
45
|
+
cap reads as its own chip side-by-side. */
|
|
46
|
+
.kbd:has(> .kbd) {
|
|
47
|
+
display: inline-flex;
|
|
48
|
+
gap: --spacing(1);
|
|
49
|
+
align-items: center;
|
|
50
|
+
padding: 0;
|
|
51
|
+
background: transparent;
|
|
52
|
+
border: 0;
|
|
53
|
+
box-shadow: none;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* @stisla/style — Link. Ported from src/scss/components/_link.scss. Reboot strips <a> color +
|
|
2
|
+
* underline globally; .link is the opt-back-in for inline links in app UI (helper text, explainers,
|
|
3
|
+
* cross-references) — the visual twin of .prose a. References the @theme tokens (colors var(--color-*),
|
|
4
|
+
* spacing/sizes --spacing(n)). Knobs are --link-*; --link-color-hover derives from --link-color so
|
|
5
|
+
* moving the color moves the hover. @layer components. Authoring rules: ../../../../PORTING.md */
|
|
6
|
+
|
|
7
|
+
@layer components {
|
|
8
|
+
.link {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: var(--link-gap, --spacing(1));
|
|
12
|
+
color: var(--link-color, var(--color-primary));
|
|
13
|
+
text-decoration: underline;
|
|
14
|
+
text-underline-offset: var(--link-decoration-offset, 2px);
|
|
15
|
+
text-decoration-thickness: var(--link-decoration-thickness, 1px);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.link svg {
|
|
19
|
+
width: --spacing(4.5);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.link:hover {
|
|
23
|
+
color: var(
|
|
24
|
+
--link-color-hover,
|
|
25
|
+
color-mix(in oklch, var(--link-color, var(--color-primary)) 80%, black)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|