@adnap/krysalicss 0.0.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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/base/global.css +141 -0
- package/dist/base/global.min.css +1 -0
- package/dist/base/reset.css +170 -0
- package/dist/base/reset.min.css +1 -0
- package/dist/element/badge.css +167 -0
- package/dist/element/badge.min.css +1 -0
- package/dist/element/button.css +250 -0
- package/dist/element/button.min.css +1 -0
- package/dist/element/checkbox.css +149 -0
- package/dist/element/checkbox.min.css +1 -0
- package/dist/element/file.css +148 -0
- package/dist/element/file.min.css +1 -0
- package/dist/element/progress.css +183 -0
- package/dist/element/progress.min.css +1 -0
- package/dist/element/radio.css +144 -0
- package/dist/element/radio.min.css +1 -0
- package/dist/element/range.css +183 -0
- package/dist/element/range.min.css +1 -0
- package/dist/element/select.css +156 -0
- package/dist/element/select.min.css +1 -0
- package/dist/element/switch.css +186 -0
- package/dist/element/switch.min.css +1 -0
- package/dist/element/text-input.css +143 -0
- package/dist/element/text-input.min.css +1 -0
- package/dist/element/textarea.css +145 -0
- package/dist/element/textarea.min.css +1 -0
- package/dist/helper/breakpoints.css +113 -0
- package/dist/helper/breakpoints.min.css +1 -0
- package/dist/helper/state.css +126 -0
- package/dist/helper/state.min.css +1 -0
- package/dist/krysalicss.css +1420 -0
- package/dist/krysalicss.css.map +1 -0
- package/dist/krysalicss.min.css +1 -0
- package/dist/layout/container.css +145 -0
- package/dist/layout/container.min.css +1 -0
- package/dist/layout/flex.css +135 -0
- package/dist/layout/flex.min.css +1 -0
- package/dist/layout/grid.css +188 -0
- package/dist/layout/grid.min.css +1 -0
- package/dist/module/alert.css +166 -0
- package/dist/module/alert.min.css +1 -0
- package/dist/module/card.css +157 -0
- package/dist/module/card.min.css +1 -0
- package/dist/module/field.css +138 -0
- package/dist/module/field.min.css +1 -0
- package/dist/module/modal.css +187 -0
- package/dist/module/modal.min.css +1 -0
- package/dist/module/navbar.css +256 -0
- package/dist/module/navbar.min.css +1 -0
- package/dist/module/tabs.css +167 -0
- package/dist/module/tabs.min.css +1 -0
- package/dist/tokens/dark.json +85 -0
- package/dist/tokens/default.json +85 -0
- package/dist/tokens/high-contrast.json +85 -0
- package/dist/typography/base.css +131 -0
- package/dist/typography/base.min.css +1 -0
- package/dist/typography/content.css +137 -0
- package/dist/typography/content.min.css +1 -0
- package/dist/typography/link.css +132 -0
- package/dist/typography/link.min.css +1 -0
- package/dist/typography/table.css +121 -0
- package/dist/typography/table.min.css +1 -0
- package/dist/typography/title.css +122 -0
- package/dist/typography/title.min.css +1 -0
- package/package.json +111 -0
- package/src/_index.scss +18 -0
- package/src/base/_index.scss +2 -0
- package/src/base/global/_index.scss +2 -0
- package/src/base/global/_plugin.scss +34 -0
- package/src/base/global/_variables.scss +11 -0
- package/src/base/reset/_index.scss +2 -0
- package/src/base/reset/_plugin.scss +70 -0
- package/src/base/reset/_variables.scss +5 -0
- package/src/element/_index.scss +11 -0
- package/src/element/badge/_index.scss +2 -0
- package/src/element/badge/_plugin.scss +44 -0
- package/src/element/badge/_variables.scss +19 -0
- package/src/element/button/_index.scss +2 -0
- package/src/element/button/_plugin.scss +173 -0
- package/src/element/button/_variables.scss +36 -0
- package/src/element/checkbox/_index.scss +2 -0
- package/src/element/checkbox/_plugin.scss +48 -0
- package/src/element/checkbox/_variables.scss +24 -0
- package/src/element/file/_index.scss +2 -0
- package/src/element/file/_plugin.scss +45 -0
- package/src/element/file/_variables.scss +27 -0
- package/src/element/progress/_index.scss +2 -0
- package/src/element/progress/_plugin.scss +96 -0
- package/src/element/progress/_variables.scss +19 -0
- package/src/element/radio/_index.scss +2 -0
- package/src/element/radio/_plugin.scss +47 -0
- package/src/element/radio/_variables.scss +16 -0
- package/src/element/range/_index.scss +2 -0
- package/src/element/range/_plugin.scss +101 -0
- package/src/element/range/_variables.scss +19 -0
- package/src/element/select/_index.scss +2 -0
- package/src/element/select/_plugin.scss +58 -0
- package/src/element/select/_variables.scss +26 -0
- package/src/element/switch/_index.scss +2 -0
- package/src/element/switch/_plugin.scss +105 -0
- package/src/element/switch/_variables.scss +28 -0
- package/src/element/text-input/_index.scss +2 -0
- package/src/element/text-input/_plugin.scss +37 -0
- package/src/element/text-input/_variables.scss +22 -0
- package/src/element/textarea/_index.scss +2 -0
- package/src/element/textarea/_plugin.scss +39 -0
- package/src/element/textarea/_variables.scss +22 -0
- package/src/helper/_breakpoints.scss +45 -0
- package/src/helper/_controls.scss +82 -0
- package/src/helper/_index.scss +3 -0
- package/src/helper/_state.scss +35 -0
- package/src/krysalicss.scss +17 -0
- package/src/layout/_index.scss +3 -0
- package/src/layout/container/_index.scss +2 -0
- package/src/layout/container/_plugin.scss +26 -0
- package/src/layout/container/_variables.scss +5 -0
- package/src/layout/flex/_index.scss +2 -0
- package/src/layout/flex/_plugin.scss +32 -0
- package/src/layout/flex/_variables.scss +9 -0
- package/src/layout/grid/_index.scss +2 -0
- package/src/layout/grid/_plugin.scss +37 -0
- package/src/layout/grid/_variables.scss +37 -0
- package/src/module/_index.scss +6 -0
- package/src/module/alert/_index.scss +2 -0
- package/src/module/alert/_plugin.scss +51 -0
- package/src/module/alert/_variables.scss +24 -0
- package/src/module/card/_index.scss +2 -0
- package/src/module/card/_plugin.scss +31 -0
- package/src/module/card/_variables.scss +13 -0
- package/src/module/field/_index.scss +2 -0
- package/src/module/field/_plugin.scss +49 -0
- package/src/module/field/_variables.scss +26 -0
- package/src/module/modal/_index.scss +2 -0
- package/src/module/modal/_plugin.scss +137 -0
- package/src/module/modal/_variables.scss +39 -0
- package/src/module/navbar/_index.scss +2 -0
- package/src/module/navbar/_plugin.scss +186 -0
- package/src/module/navbar/_variables.scss +49 -0
- package/src/module/tabs/_index.scss +2 -0
- package/src/module/tabs/_plugin.scss +106 -0
- package/src/module/tabs/_variables.scss +16 -0
- package/src/theme/_create.scss +74 -0
- package/src/theme/dark.scss +51 -0
- package/src/theme/default.scss +45 -0
- package/src/theme/high-contrast.scss +53 -0
- package/src/typography/_index.scss +5 -0
- package/src/typography/base/_index.scss +2 -0
- package/src/typography/base/_plugin.scss +21 -0
- package/src/typography/base/_variables.scss +15 -0
- package/src/typography/content/_index.scss +2 -0
- package/src/typography/content/_plugin.scss +39 -0
- package/src/typography/content/_variables.scss +1 -0
- package/src/typography/link/_index.scss +2 -0
- package/src/typography/link/_mixins.scss +29 -0
- package/src/typography/link/_plugin.scss +15 -0
- package/src/typography/link/_variables.scss +15 -0
- package/src/typography/table/_index.scss +2 -0
- package/src/typography/table/_plugin.scss +13 -0
- package/src/typography/table/_variables.scss +6 -0
- package/src/typography/title/_index.scss +2 -0
- package/src/typography/title/_plugin.scss +36 -0
- package/src/typography/title/_variables.scss +7 -0
- package/src/variables/_color.scss +66 -0
- package/src/variables/_default.scss +52 -0
- package/src/variables/_feature.scss +39 -0
- package/src/variables/_global.scss +93 -0
- package/src/variables/_index.scss +7 -0
- package/src/variables/_responsive.scss +10 -0
- package/src/variables/_selector.scss +1 -0
- package/src/variables/_size.scss +5 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
|
|
3
|
+
$selector: ".field" !default;
|
|
4
|
+
$label-selector: ".field__label" !default;
|
|
5
|
+
$help-selector: ".field__help" !default;
|
|
6
|
+
$invalid-selector: "#{variables.$selector-prefix-is}invalid" !default;
|
|
7
|
+
$disabled-selector: "#{variables.$selector-prefix-is}disabled" !default;
|
|
8
|
+
|
|
9
|
+
// Vertical rhythm: gap stacks label + control + help with consistent spacing.
|
|
10
|
+
$gap: variables.$size-extra-small !default;
|
|
11
|
+
|
|
12
|
+
// Help text de-emphasised vs. body copy.
|
|
13
|
+
$help-font-size: 0.85em !default;
|
|
14
|
+
$help-mix: 70% !default;
|
|
15
|
+
|
|
16
|
+
// Bottom margin between adjacent fields. Equal to the global gap so successive
|
|
17
|
+
// `.field`s in a flow read as a coherent stack.
|
|
18
|
+
$margin-bottom: variables.$global-gap !default;
|
|
19
|
+
|
|
20
|
+
// Non-colour cue prepended to the label when the field contains an invalid
|
|
21
|
+
// control. Satisfies WCAG 1.4.1 (Use of Colour): the danger-tinted border
|
|
22
|
+
// alone is colour-only AND only 2.8:1 in light theme (misses 1.4.11). The
|
|
23
|
+
// glyph reuses the `--kc-state-icon-danger` custom-property hook from
|
|
24
|
+
// `helper/state` so consumers swapping the global icon get it here too.
|
|
25
|
+
// Trailing space is U+0020 so the icon visually separates from the label.
|
|
26
|
+
$invalid-icon: "✕ " !default;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
@use "sass:list";
|
|
2
|
+
@use "variables" as *;
|
|
3
|
+
@use "../../variables";
|
|
4
|
+
|
|
5
|
+
@mixin _backdrop-styles {
|
|
6
|
+
@include variables.backgroundColor(modal-backdrop, $backdrop-color);
|
|
7
|
+
@if $backdrop-blur != 0 {
|
|
8
|
+
backdrop-filter: blur($backdrop-blur);
|
|
9
|
+
-webkit-backdrop-filter: blur($backdrop-blur);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@if variables.$feature-module-modal {
|
|
14
|
+
// `:target`-driven open state. `<a href="#dialog-id">` triggers the
|
|
15
|
+
// browser to skip `display: none` on the matched <dialog>. Pulled out of
|
|
16
|
+
// the framework when `$controls` doesn't include `'css'`.
|
|
17
|
+
@if variables.has-controls("css", $controls) {
|
|
18
|
+
// Reserve scrollbar gutter on any page containing a modal so opening a
|
|
19
|
+
// :target-opened modal (which sets `overflow: hidden` to lock background
|
|
20
|
+
// scroll) doesn't yank the scrollbar away and shift page content right.
|
|
21
|
+
// showModal() doesn't need this because the UA handles scroll lock
|
|
22
|
+
// internally without touching layout.
|
|
23
|
+
html:has(#{$selector}) {
|
|
24
|
+
scrollbar-gutter: stable;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Backdrop + scroll lock for :target-opened modals. The backdrop pseudo
|
|
28
|
+
// lives on `html`, NOT on the dialog itself: keeping it outside the
|
|
29
|
+
// dialog's stacking context matches how the UA's ::backdrop sits
|
|
30
|
+
// relative to a showModal()-opened dialog. If the backdrop pseudo sat
|
|
31
|
+
// inside the dialog (z-index: -1 under z-index: 100), the dialog's
|
|
32
|
+
// `box-shadow` would paint in the same context, extending outside the
|
|
33
|
+
// dialog box and compositing with the backdrop below: a visibly darker
|
|
34
|
+
// ring around the modal that didn't appear on the showModal() version.
|
|
35
|
+
html:has(#{$selector}:target) {
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
|
|
38
|
+
&::before {
|
|
39
|
+
content: "";
|
|
40
|
+
position: fixed;
|
|
41
|
+
inset: 0;
|
|
42
|
+
@include _backdrop-styles;
|
|
43
|
+
z-index: 99;
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#{$selector} {
|
|
50
|
+
@include variables.backgroundColor(
|
|
51
|
+
modal-bg,
|
|
52
|
+
list.nth($default-combination, 1),
|
|
53
|
+
);
|
|
54
|
+
@include variables.textColor(modal-fg, list.nth($default-combination, 2));
|
|
55
|
+
width: min(100% - 2rem, $max-width);
|
|
56
|
+
padding: $padding;
|
|
57
|
+
border: $border;
|
|
58
|
+
border-radius: $border-radius;
|
|
59
|
+
box-shadow: 0 12px 32px color-mix(in srgb, #000000 32%, transparent);
|
|
60
|
+
|
|
61
|
+
// Native `dialog::backdrop` for `showModal()`-opened dialogs. Only
|
|
62
|
+
// emitted when `$controls` includes `'js'`: a `:target`-only build
|
|
63
|
+
// never invokes `.showModal()`, so the UA pseudo never paints.
|
|
64
|
+
@if variables.has-controls("js", $controls) {
|
|
65
|
+
&::backdrop {
|
|
66
|
+
@include _backdrop-styles;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#{$content-selector} {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
gap: $gap;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#{$header-selector} {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: space-between;
|
|
80
|
+
gap: $gap;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#{$body-selector} {
|
|
84
|
+
flex: 1 1 auto;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#{$footer-selector} {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-wrap: wrap;
|
|
90
|
+
gap: $gap;
|
|
91
|
+
justify-content: flex-end;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// JS-free reveal + click-outside dismiss. Backdrop and scroll lock are
|
|
95
|
+
// emitted on `html` above so the dialog's stacking context stays clean
|
|
96
|
+
// (avoids the `box-shadow` + backdrop compositing ring). Trade-offs vs
|
|
97
|
+
// showModal() (no focus trap, no ESC-to-close, no inert background) are
|
|
98
|
+
// documented in the modal reference page.
|
|
99
|
+
@if variables.has-controls("css", $controls) {
|
|
100
|
+
&:target {
|
|
101
|
+
display: block;
|
|
102
|
+
// Match the UA `:modal` centring used by showModal()-opened dialogs:
|
|
103
|
+
// fixed, all four insets at 0, then margin: auto distributes the
|
|
104
|
+
// leftover viewport space on both axes around the content-sized
|
|
105
|
+
// dialog. Same anchor point and same pixel rounding as the JS path,
|
|
106
|
+
// so the two demos are visually identical.
|
|
107
|
+
position: fixed;
|
|
108
|
+
inset: 0;
|
|
109
|
+
margin: auto;
|
|
110
|
+
// Width is shared with the base rule; height shrinks to content so
|
|
111
|
+
// margin-block: auto can distribute vertical space.
|
|
112
|
+
height: fit-content;
|
|
113
|
+
z-index: 100;
|
|
114
|
+
|
|
115
|
+
// Opt-in click-outside-to-close. The consumer adds an
|
|
116
|
+
// `<a href="#" class="modal__dismiss" aria-label="Close"></a>`
|
|
117
|
+
// anywhere inside the dialog; it stretches to cover the viewport
|
|
118
|
+
// behind the content and a click navigates the hash away.
|
|
119
|
+
#{$dismiss-selector} {
|
|
120
|
+
display: block;
|
|
121
|
+
position: fixed;
|
|
122
|
+
inset: 0;
|
|
123
|
+
z-index: -1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The dismiss anchor is irrelevant outside the `:target` open path
|
|
128
|
+
// (showModal() consumers use ESC or a close button); collapse it so
|
|
129
|
+
// it doesn't claim layout while the modal is closed or
|
|
130
|
+
// showModal()-opened. Paired with `:target` above so a JS-only build
|
|
131
|
+
// doesn't bother emitting the rule.
|
|
132
|
+
#{$dismiss-selector} {
|
|
133
|
+
display: none;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
|
|
3
|
+
$selector: ".modal" !default;
|
|
4
|
+
$content-selector: ".modal__content" !default;
|
|
5
|
+
$header-selector: ".modal__header" !default;
|
|
6
|
+
$body-selector: ".modal__body" !default;
|
|
7
|
+
$footer-selector: ".modal__footer" !default;
|
|
8
|
+
|
|
9
|
+
// Optional click-outside dismiss anchor for the `:target` open path.
|
|
10
|
+
// When the consumer places `<a href="#" class="modal__dismiss"></a>`
|
|
11
|
+
// inside the dialog, it stretches to fill the viewport behind the
|
|
12
|
+
// content and clicking it navigates the hash away (closing the modal).
|
|
13
|
+
$dismiss-selector: ".modal__dismiss" !default;
|
|
14
|
+
|
|
15
|
+
$max-width: 32rem !default;
|
|
16
|
+
$padding: variables.$size-normal !default;
|
|
17
|
+
$gap: variables.$size-small !default;
|
|
18
|
+
$border-radius: variables.$global-border-radius !default;
|
|
19
|
+
|
|
20
|
+
// `<dialog>` UA styles place a thin black border on Chromium; reset it.
|
|
21
|
+
$border: 0 !default;
|
|
22
|
+
|
|
23
|
+
// Backdrop tint. SCSS literal here is the fallback; the plugin layers the
|
|
24
|
+
// `--kc-modal-backdrop` custom property over it so themes can flip the
|
|
25
|
+
// backdrop per scheme (dark theme uses a translucent-light wash so the
|
|
26
|
+
// dark surface stands out) without recompiling consumers.
|
|
27
|
+
$backdrop-color: color-mix(in srgb, #000000 55%, transparent) !default;
|
|
28
|
+
|
|
29
|
+
// Blur applied to content behind the backdrop. Set to `0` to disable.
|
|
30
|
+
$backdrop-blur: 6px !default;
|
|
31
|
+
|
|
32
|
+
$default-combination: variables.$color-combination-default !default;
|
|
33
|
+
|
|
34
|
+
// Reveal mechanism for the modal. `'css'` emits the JS-free `:target` path
|
|
35
|
+
// (backdrop, scroll lock, centring, click-outside dismiss). `'js'` emits the
|
|
36
|
+
// `dialog[open]` companion plus the native `::backdrop` pseudo for
|
|
37
|
+
// `showModal()`-opened dialogs. Defaults to `$global-controls`; override to
|
|
38
|
+
// `'js'` for a JS-only build that strips the `:target` rules.
|
|
39
|
+
$controls: variables.$global-controls !default;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
@use "sass:list";
|
|
2
|
+
@use "variables" as *;
|
|
3
|
+
@use "../../variables";
|
|
4
|
+
@use "../../helper/breakpoints" as breakpoints;
|
|
5
|
+
|
|
6
|
+
@if variables.$feature-module-navbar {
|
|
7
|
+
#{$selector} {
|
|
8
|
+
// Track (--kc-bg, --kc-fg) by default so the navbar follows the active
|
|
9
|
+
// theme. Pre-fix the navbar pinned to the default combination
|
|
10
|
+
// (white, near-black) regardless of theme, which painted near-black
|
|
11
|
+
// text on a near-black header in dark themes (1.25:1, WCAG 1.4.3 fail).
|
|
12
|
+
// To restore brand-pinned behaviour, use a combination modifier
|
|
13
|
+
// (`<nav class="navbar is-primary">`) or override --kc-navbar-bg /
|
|
14
|
+
// --kc-navbar-fg directly.
|
|
15
|
+
@include variables.backgroundColor(
|
|
16
|
+
navbar-bg,
|
|
17
|
+
var(--kc-bg, #{list.nth($default-combination, 1)}),
|
|
18
|
+
);
|
|
19
|
+
@include variables.textColor(
|
|
20
|
+
navbar-fg,
|
|
21
|
+
var(--kc-fg, #{list.nth($default-combination, 2)}),
|
|
22
|
+
);
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
gap: $gap;
|
|
26
|
+
padding: $padding-y $padding-x;
|
|
27
|
+
border-radius: $border-radius;
|
|
28
|
+
|
|
29
|
+
// Shared affordance for any anchor / button / label that acts as
|
|
30
|
+
// navigation chrome (brand + items). Without this the brand inherits
|
|
31
|
+
// the global `<a>` underline and `--kc-link` colour, which clashes
|
|
32
|
+
// with the navbar surface — and goes near-invisible against pinned
|
|
33
|
+
// brand backgrounds (`.is-primary` paints `--kc-link` on its own
|
|
34
|
+
// base colour).
|
|
35
|
+
#{$brand-selector},
|
|
36
|
+
#{$link-selector} {
|
|
37
|
+
display: inline-block;
|
|
38
|
+
padding: $item-padding-y $item-padding-x;
|
|
39
|
+
border-radius: $border-radius;
|
|
40
|
+
text-decoration: none;
|
|
41
|
+
color: inherit;
|
|
42
|
+
|
|
43
|
+
&:focus-visible {
|
|
44
|
+
outline: $focus-outline-width $focus-outline-style
|
|
45
|
+
var(--kc-focus-ring, currentColor);
|
|
46
|
+
outline-offset: $focus-outline-offset;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&:hover {
|
|
50
|
+
background: $hover-bg;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#{$brand-selector} {
|
|
55
|
+
font-weight: 600;
|
|
56
|
+
flex: 0 0 auto;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#{$menu-selector} {
|
|
60
|
+
list-style: none;
|
|
61
|
+
margin: 0;
|
|
62
|
+
padding: 0;
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-wrap: wrap;
|
|
65
|
+
gap: $gap;
|
|
66
|
+
flex: 1 1 auto;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#{$item-selector} {
|
|
70
|
+
position: relative;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#{$link-selector} {
|
|
74
|
+
// When the navbar link is a <button> (typical for a dropdown trigger),
|
|
75
|
+
// opt out of the framework's `button, .button` defaults so it reads
|
|
76
|
+
// as a menu item, not a chunky button. Overrides background, border,
|
|
77
|
+
// min-width, font, and cursor that the button rule would otherwise
|
|
78
|
+
// apply at equal specificity.
|
|
79
|
+
background: transparent;
|
|
80
|
+
border: 0;
|
|
81
|
+
min-width: 0;
|
|
82
|
+
font: inherit;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#{$item-selector}#{$has-dropdown-modifier} {
|
|
87
|
+
// Visually hide the toggle but keep it focusable / clickable via the
|
|
88
|
+
// paired <label for>. Same clip-rect technique as `tabs/_plugin.scss`.
|
|
89
|
+
#{$dropdown-toggle-selector} {
|
|
90
|
+
position: absolute;
|
|
91
|
+
width: 1px;
|
|
92
|
+
height: 1px;
|
|
93
|
+
padding: 0;
|
|
94
|
+
margin: -1px;
|
|
95
|
+
overflow: hidden;
|
|
96
|
+
clip: rect(0, 0, 0, 0);
|
|
97
|
+
white-space: nowrap;
|
|
98
|
+
border: 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#{$dropdown-selector} {
|
|
102
|
+
display: none;
|
|
103
|
+
position: absolute;
|
|
104
|
+
inset-block-start: 100%;
|
|
105
|
+
inset-inline-start: 0;
|
|
106
|
+
min-width: $dropdown-min-width;
|
|
107
|
+
padding: $dropdown-padding;
|
|
108
|
+
margin: 0;
|
|
109
|
+
list-style: none;
|
|
110
|
+
border-radius: $border-radius;
|
|
111
|
+
// Track (--kc-bg, --kc-fg) so the dropdown panel matches the
|
|
112
|
+
// active theme. The navbar surface itself was made theme-reactive
|
|
113
|
+
// earlier; the dropdown panel was left pinned to the default
|
|
114
|
+
// combination, which painted white-on-near-black in dark theme
|
|
115
|
+
// against a dark navbar (visually jarring, near-illegible).
|
|
116
|
+
@include variables.backgroundColor(
|
|
117
|
+
navbar-dropdown-bg,
|
|
118
|
+
var(--kc-bg, #{list.nth($default-combination, 1)}),
|
|
119
|
+
);
|
|
120
|
+
@include variables.textColor(
|
|
121
|
+
navbar-dropdown-fg,
|
|
122
|
+
var(--kc-fg, #{list.nth($default-combination, 2)}),
|
|
123
|
+
);
|
|
124
|
+
border: 1px solid color-mix(in srgb, currentColor 12%, transparent);
|
|
125
|
+
box-shadow: 0 4px 12px
|
|
126
|
+
color-mix(in srgb, currentColor 18%, transparent);
|
|
127
|
+
z-index: 10;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@if variables.has-controls("css", $controls) {
|
|
131
|
+
&:has(#{$dropdown-toggle-selector}:checked) #{$dropdown-selector} {
|
|
132
|
+
display: block;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@if variables.has-controls("js", $controls) {
|
|
137
|
+
&:has([aria-expanded="true"]) #{$dropdown-selector} {
|
|
138
|
+
display: block;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@if variables.has-controls("css", $controls) {
|
|
143
|
+
&#{$hoverable-modifier} {
|
|
144
|
+
&:hover #{$dropdown-selector},
|
|
145
|
+
&:focus-within #{$dropdown-selector} {
|
|
146
|
+
display: block;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#{$toggle-selector} {
|
|
153
|
+
display: none;
|
|
154
|
+
flex: 0 0 auto;
|
|
155
|
+
margin-inline-start: auto;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@include breakpoints.until($mobile-breakpoint) {
|
|
159
|
+
flex-wrap: wrap;
|
|
160
|
+
|
|
161
|
+
#{$toggle-selector} {
|
|
162
|
+
display: inline-block;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// The default mobile layout assumes the menu is wrapped in a
|
|
166
|
+
// `<details>` so its `<summary>` provides the toggle. The menu sits
|
|
167
|
+
// below the brand row and stacks vertically.
|
|
168
|
+
#{$menu-selector} {
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
flex-basis: 100%;
|
|
171
|
+
gap: $item-padding-y;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#{$item-selector}#{$has-dropdown-modifier} #{$dropdown-selector} {
|
|
175
|
+
position: static;
|
|
176
|
+
box-shadow: none;
|
|
177
|
+
border: 0;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@include variables.combinations using ($label, $tuple) {
|
|
182
|
+
@include variables.backgroundColor(#{$label}-bg, list.nth($tuple, 1));
|
|
183
|
+
@include variables.textColor(#{$label}-fg, list.nth($tuple, 2));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
|
|
3
|
+
$selector: ".navbar" !default;
|
|
4
|
+
$brand-selector: ".navbar__brand" !default;
|
|
5
|
+
$menu-selector: ".navbar__menu" !default;
|
|
6
|
+
$item-selector: ".navbar__item" !default;
|
|
7
|
+
$link-selector: ".navbar__link" !default;
|
|
8
|
+
$dropdown-selector: ".navbar__dropdown" !default;
|
|
9
|
+
$toggle-selector: ".navbar__toggle" !default;
|
|
10
|
+
$dropdown-toggle-selector: ".navbar__dropdown-toggle" !default;
|
|
11
|
+
$hoverable-modifier: ".hoverable" !default;
|
|
12
|
+
|
|
13
|
+
// Modifier on `$item-selector` that opts the row into a focus-within
|
|
14
|
+
// dropdown. Without it, items render inline only: adding the dropdown
|
|
15
|
+
// styles unconditionally would force every link into an over-tall row.
|
|
16
|
+
$has-dropdown-modifier: ".has-dropdown" !default;
|
|
17
|
+
|
|
18
|
+
// Below this device, the menu collapses behind a `<details>` toggle.
|
|
19
|
+
// Above, it renders inline horizontally.
|
|
20
|
+
$mobile-breakpoint: tablet !default;
|
|
21
|
+
|
|
22
|
+
$gap: variables.$global-gap !default;
|
|
23
|
+
$padding-y: variables.$size-small !default;
|
|
24
|
+
$padding-x: variables.$size-normal !default;
|
|
25
|
+
$item-padding-y: variables.$size-extra-small !default;
|
|
26
|
+
$item-padding-x: variables.$size-small !default;
|
|
27
|
+
$border-radius: variables.$global-border-radius !default;
|
|
28
|
+
|
|
29
|
+
$focus-outline-width: variables.$global-focus-outline-width !default;
|
|
30
|
+
$focus-outline-style: variables.$global-focus-outline-style !default;
|
|
31
|
+
$focus-outline-offset: variables.$global-focus-outline-offset !default;
|
|
32
|
+
|
|
33
|
+
// Subtle on-hover backdrop tint for links + brand. `currentColor` is
|
|
34
|
+
// theme-reactive (it follows the navbar fg), and the low alpha keeps
|
|
35
|
+
// the affordance unobtrusive against any combination surface.
|
|
36
|
+
$hover-bg: color-mix(in srgb, currentColor 8%, transparent) !default;
|
|
37
|
+
|
|
38
|
+
// Stack distance for an open dropdown panel; mirrors `.card`'s edge so
|
|
39
|
+
// the menu visually clears the trigger row without a manual offset.
|
|
40
|
+
$dropdown-min-width: 12rem !default;
|
|
41
|
+
$dropdown-padding: variables.$size-extra-small !default;
|
|
42
|
+
|
|
43
|
+
$default-combination: variables.$color-combination-default !default;
|
|
44
|
+
|
|
45
|
+
// Reveal mechanism for the dropdown panel. Defaults to the framework-wide
|
|
46
|
+
// `$global-controls`, but can be retuned per-component (e.g. ship the
|
|
47
|
+
// navbar with JS hooks only while the rest of the framework keeps the CSS
|
|
48
|
+
// branch).
|
|
49
|
+
$controls: variables.$global-controls !default;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
@use "sass:list";
|
|
2
|
+
@use "variables" as *;
|
|
3
|
+
@use "../../variables";
|
|
4
|
+
@use "../../helper/controls" as controls;
|
|
5
|
+
|
|
6
|
+
// JS-free pattern. Markup contract: each tab is a flat sequence of
|
|
7
|
+
// input → label → panel siblings inside `.tabs`. The visible panel is
|
|
8
|
+
// the one immediately following the checked input's label.
|
|
9
|
+
//
|
|
10
|
+
// <div class="tabs">
|
|
11
|
+
// <input class="tabs__input" type="radio" name="t" id="t-1" checked>
|
|
12
|
+
// <label class="tabs__tab" for="t-1">One</label>
|
|
13
|
+
// <div class="tabs__panel">Panel 1</div>
|
|
14
|
+
//
|
|
15
|
+
// <input class="tabs__input" type="radio" name="t" id="t-2">
|
|
16
|
+
// <label class="tabs__tab" for="t-2">Two</label>
|
|
17
|
+
// <div class="tabs__panel">Panel 2</div>
|
|
18
|
+
// </div>
|
|
19
|
+
//
|
|
20
|
+
// This avoids per-index enumeration: the adjacent-sibling chain
|
|
21
|
+
// `input:checked + label + panel` matches whichever pair is active,
|
|
22
|
+
// regardless of how many tabs the markup contains.
|
|
23
|
+
|
|
24
|
+
@if variables.$feature-module-tabs {
|
|
25
|
+
#{$selector} {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
gap: $gap;
|
|
29
|
+
|
|
30
|
+
// Visually hidden but kept focusable / clickable via the paired
|
|
31
|
+
// <label for>. position: absolute takes the input out of flex flow
|
|
32
|
+
// so it doesn't claim a row of its own.
|
|
33
|
+
#{$input-selector} {
|
|
34
|
+
position: absolute;
|
|
35
|
+
width: 1px;
|
|
36
|
+
height: 1px;
|
|
37
|
+
padding: 0;
|
|
38
|
+
margin: -1px;
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
clip: rect(0, 0, 0, 0);
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
border: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#{$tab-selector} {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
padding: $padding-y $padding-x;
|
|
48
|
+
// Top corners only: the bottom edge sits on the active-tab underline.
|
|
49
|
+
// `border-start-start-radius` / `border-start-end-radius` keep that
|
|
50
|
+
// shape correct in any writing-mode (top corners flip with vertical-rl).
|
|
51
|
+
border-start-start-radius: $border-radius;
|
|
52
|
+
border-start-end-radius: $border-radius;
|
|
53
|
+
// Explicitly square the bottom corners so a `<button class="tabs__tab">`
|
|
54
|
+
// (JS-enhanced WAI-ARIA tablist variant) doesn't inherit the UA's
|
|
55
|
+
// four-corner radius and end up shaped differently from a `<label>`.
|
|
56
|
+
border-end-start-radius: 0;
|
|
57
|
+
border-end-end-radius: 0;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
// Reset the UA chrome so `<button>` and `<label>` render identically:
|
|
60
|
+
// transparent fill (no UA "ButtonFace" + hover bg), inherited typography
|
|
61
|
+
// (UA forces small-caps font on buttons), and only the active-tab
|
|
62
|
+
// underline as a visible edge.
|
|
63
|
+
appearance: none;
|
|
64
|
+
background: transparent;
|
|
65
|
+
color: inherit;
|
|
66
|
+
font: inherit;
|
|
67
|
+
border: 0;
|
|
68
|
+
border-block-end: 2px solid transparent;
|
|
69
|
+
user-select: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#{$input-selector}:checked + #{$tab-selector} {
|
|
73
|
+
@include variables.backgroundColor(
|
|
74
|
+
tabs-active-bg,
|
|
75
|
+
list.nth($active-combination, 1),
|
|
76
|
+
);
|
|
77
|
+
@include variables.textColor(
|
|
78
|
+
tabs-active-fg,
|
|
79
|
+
list.nth($active-combination, 2),
|
|
80
|
+
);
|
|
81
|
+
border-block-end-color: currentColor;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#{$input-selector}:focus-visible + #{$tab-selector} {
|
|
85
|
+
@include controls.focus-ring();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// `order: 999` keeps every panel after all tabs in the visual flex
|
|
89
|
+
// order, regardless of DOM position. flex: 1 1 100% claims the whole
|
|
90
|
+
// row, so the visible panel wraps to its own line beneath the tab
|
|
91
|
+
// strip; hidden panels (display: none) are out of flow.
|
|
92
|
+
#{$panel-selector} {
|
|
93
|
+
order: 999;
|
|
94
|
+
flex: 1 1 100%;
|
|
95
|
+
padding: $panel-padding;
|
|
96
|
+
display: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Single reveal rule, no enumeration: when an input is checked, the
|
|
100
|
+
// immediately-following label and that label's immediately-following
|
|
101
|
+
// panel paint. Works for any number of tabs.
|
|
102
|
+
#{$input-selector}:checked + #{$tab-selector} + #{$panel-selector} {
|
|
103
|
+
display: block;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@use "../../variables";
|
|
2
|
+
|
|
3
|
+
$selector: ".tabs" !default;
|
|
4
|
+
$input-selector: ".tabs__input" !default;
|
|
5
|
+
$tab-selector: ".tabs__tab" !default;
|
|
6
|
+
$panel-selector: ".tabs__panel" !default;
|
|
7
|
+
|
|
8
|
+
$gap: variables.$size-extra-small !default;
|
|
9
|
+
$padding-y: variables.$size-small !default;
|
|
10
|
+
$padding-x: variables.$size-small !default;
|
|
11
|
+
$border-radius: variables.$global-border-radius !default;
|
|
12
|
+
$panel-padding: variables.$size-normal !default;
|
|
13
|
+
|
|
14
|
+
// Active tab adopts the default combination; inactive tabs stay
|
|
15
|
+
// transparent so the row reads as an underline list.
|
|
16
|
+
$active-combination: variables.$color-combination-default !default;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
@use "sass:list";
|
|
2
|
+
@use "sass:map";
|
|
3
|
+
@use "../variables/color" as color;
|
|
4
|
+
@use "../variables/default" as default-vars;
|
|
5
|
+
|
|
6
|
+
// Single source of truth for the custom-property prefix lives in
|
|
7
|
+
// variables/_default.scss; kept here as a forwarded alias only.
|
|
8
|
+
$prefix: default-vars.$prefix;
|
|
9
|
+
|
|
10
|
+
// $name theme name string. Reserved for diagnostics; currently
|
|
11
|
+
// unused but kept for API stability.
|
|
12
|
+
// $variables map of scalar token short-name → value (no `--kc-`
|
|
13
|
+
// prefix). Optional — keys absent from the map are not
|
|
14
|
+
// emitted; consumers fall back to each component's
|
|
15
|
+
// hardcoded SCSS-time default. Typos produce silent
|
|
16
|
+
// partial themes, not compile errors.
|
|
17
|
+
// $combinations map of combination label → (bg, fg) tuple. Optional;
|
|
18
|
+
// same fallback semantics. Use
|
|
19
|
+
// `map.merge(variables.$color-combinations, …)` to
|
|
20
|
+
// override individual entries while keeping the rest.
|
|
21
|
+
// $is-default emit unconditionally on :root when true.
|
|
22
|
+
// $root-when optional media-query string ("prefers-color-scheme: dark");
|
|
23
|
+
// when set, tokens are also emitted under that @media :root.
|
|
24
|
+
// $modifier-class optional class selector (".theme-dark"); when set, tokens
|
|
25
|
+
// are also emitted there for explicit overrides.
|
|
26
|
+
@mixin theme(
|
|
27
|
+
$name,
|
|
28
|
+
$variables: (),
|
|
29
|
+
$combinations: (),
|
|
30
|
+
$is-default: false,
|
|
31
|
+
$root-when: null,
|
|
32
|
+
$modifier-class: null,
|
|
33
|
+
) {
|
|
34
|
+
// When a theme ships its own `$combinations` map, surface any key drift
|
|
35
|
+
// against the canonical roster as a `@warn`. Silent partial-themes are
|
|
36
|
+
// intentional (consumers may opt out of certain combinations), so this
|
|
37
|
+
// is non-fatal — but a missing `success` / `warning` / `danger` key on
|
|
38
|
+
// a theme that's otherwise complete is almost always a typo.
|
|
39
|
+
@if list.length(map.keys($combinations)) > 0 {
|
|
40
|
+
@each $key in color.$combinations-keys {
|
|
41
|
+
@if not map.has-key($combinations, $key) {
|
|
42
|
+
@warn 'theme(#{$name}): $combinations is missing key "#{$key}"; consumers will fall back to component defaults for `.is-#{$key}`.';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@if $is-default {
|
|
48
|
+
:root {
|
|
49
|
+
@include _emit($variables, $combinations);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
@if $root-when {
|
|
53
|
+
@media (#{$root-when}) {
|
|
54
|
+
:root {
|
|
55
|
+
@include _emit($variables, $combinations);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
@if $modifier-class {
|
|
60
|
+
#{$modifier-class} {
|
|
61
|
+
@include _emit($variables, $combinations);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@mixin _emit($variables, $combinations) {
|
|
67
|
+
@each $key, $value in $variables {
|
|
68
|
+
#{$prefix}#{$key}: #{$value};
|
|
69
|
+
}
|
|
70
|
+
@each $label, $tuple in $combinations {
|
|
71
|
+
#{$prefix}#{$label}-bg: #{list.nth($tuple, 1)};
|
|
72
|
+
#{$prefix}#{$label}-fg: #{list.nth($tuple, 2)};
|
|
73
|
+
}
|
|
74
|
+
}
|