@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.
Files changed (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/base/global.css +141 -0
  4. package/dist/base/global.min.css +1 -0
  5. package/dist/base/reset.css +170 -0
  6. package/dist/base/reset.min.css +1 -0
  7. package/dist/element/badge.css +167 -0
  8. package/dist/element/badge.min.css +1 -0
  9. package/dist/element/button.css +250 -0
  10. package/dist/element/button.min.css +1 -0
  11. package/dist/element/checkbox.css +149 -0
  12. package/dist/element/checkbox.min.css +1 -0
  13. package/dist/element/file.css +148 -0
  14. package/dist/element/file.min.css +1 -0
  15. package/dist/element/progress.css +183 -0
  16. package/dist/element/progress.min.css +1 -0
  17. package/dist/element/radio.css +144 -0
  18. package/dist/element/radio.min.css +1 -0
  19. package/dist/element/range.css +183 -0
  20. package/dist/element/range.min.css +1 -0
  21. package/dist/element/select.css +156 -0
  22. package/dist/element/select.min.css +1 -0
  23. package/dist/element/switch.css +186 -0
  24. package/dist/element/switch.min.css +1 -0
  25. package/dist/element/text-input.css +143 -0
  26. package/dist/element/text-input.min.css +1 -0
  27. package/dist/element/textarea.css +145 -0
  28. package/dist/element/textarea.min.css +1 -0
  29. package/dist/helper/breakpoints.css +113 -0
  30. package/dist/helper/breakpoints.min.css +1 -0
  31. package/dist/helper/state.css +126 -0
  32. package/dist/helper/state.min.css +1 -0
  33. package/dist/krysalicss.css +1420 -0
  34. package/dist/krysalicss.css.map +1 -0
  35. package/dist/krysalicss.min.css +1 -0
  36. package/dist/layout/container.css +145 -0
  37. package/dist/layout/container.min.css +1 -0
  38. package/dist/layout/flex.css +135 -0
  39. package/dist/layout/flex.min.css +1 -0
  40. package/dist/layout/grid.css +188 -0
  41. package/dist/layout/grid.min.css +1 -0
  42. package/dist/module/alert.css +166 -0
  43. package/dist/module/alert.min.css +1 -0
  44. package/dist/module/card.css +157 -0
  45. package/dist/module/card.min.css +1 -0
  46. package/dist/module/field.css +138 -0
  47. package/dist/module/field.min.css +1 -0
  48. package/dist/module/modal.css +187 -0
  49. package/dist/module/modal.min.css +1 -0
  50. package/dist/module/navbar.css +256 -0
  51. package/dist/module/navbar.min.css +1 -0
  52. package/dist/module/tabs.css +167 -0
  53. package/dist/module/tabs.min.css +1 -0
  54. package/dist/tokens/dark.json +85 -0
  55. package/dist/tokens/default.json +85 -0
  56. package/dist/tokens/high-contrast.json +85 -0
  57. package/dist/typography/base.css +131 -0
  58. package/dist/typography/base.min.css +1 -0
  59. package/dist/typography/content.css +137 -0
  60. package/dist/typography/content.min.css +1 -0
  61. package/dist/typography/link.css +132 -0
  62. package/dist/typography/link.min.css +1 -0
  63. package/dist/typography/table.css +121 -0
  64. package/dist/typography/table.min.css +1 -0
  65. package/dist/typography/title.css +122 -0
  66. package/dist/typography/title.min.css +1 -0
  67. package/package.json +111 -0
  68. package/src/_index.scss +18 -0
  69. package/src/base/_index.scss +2 -0
  70. package/src/base/global/_index.scss +2 -0
  71. package/src/base/global/_plugin.scss +34 -0
  72. package/src/base/global/_variables.scss +11 -0
  73. package/src/base/reset/_index.scss +2 -0
  74. package/src/base/reset/_plugin.scss +70 -0
  75. package/src/base/reset/_variables.scss +5 -0
  76. package/src/element/_index.scss +11 -0
  77. package/src/element/badge/_index.scss +2 -0
  78. package/src/element/badge/_plugin.scss +44 -0
  79. package/src/element/badge/_variables.scss +19 -0
  80. package/src/element/button/_index.scss +2 -0
  81. package/src/element/button/_plugin.scss +173 -0
  82. package/src/element/button/_variables.scss +36 -0
  83. package/src/element/checkbox/_index.scss +2 -0
  84. package/src/element/checkbox/_plugin.scss +48 -0
  85. package/src/element/checkbox/_variables.scss +24 -0
  86. package/src/element/file/_index.scss +2 -0
  87. package/src/element/file/_plugin.scss +45 -0
  88. package/src/element/file/_variables.scss +27 -0
  89. package/src/element/progress/_index.scss +2 -0
  90. package/src/element/progress/_plugin.scss +96 -0
  91. package/src/element/progress/_variables.scss +19 -0
  92. package/src/element/radio/_index.scss +2 -0
  93. package/src/element/radio/_plugin.scss +47 -0
  94. package/src/element/radio/_variables.scss +16 -0
  95. package/src/element/range/_index.scss +2 -0
  96. package/src/element/range/_plugin.scss +101 -0
  97. package/src/element/range/_variables.scss +19 -0
  98. package/src/element/select/_index.scss +2 -0
  99. package/src/element/select/_plugin.scss +58 -0
  100. package/src/element/select/_variables.scss +26 -0
  101. package/src/element/switch/_index.scss +2 -0
  102. package/src/element/switch/_plugin.scss +105 -0
  103. package/src/element/switch/_variables.scss +28 -0
  104. package/src/element/text-input/_index.scss +2 -0
  105. package/src/element/text-input/_plugin.scss +37 -0
  106. package/src/element/text-input/_variables.scss +22 -0
  107. package/src/element/textarea/_index.scss +2 -0
  108. package/src/element/textarea/_plugin.scss +39 -0
  109. package/src/element/textarea/_variables.scss +22 -0
  110. package/src/helper/_breakpoints.scss +45 -0
  111. package/src/helper/_controls.scss +82 -0
  112. package/src/helper/_index.scss +3 -0
  113. package/src/helper/_state.scss +35 -0
  114. package/src/krysalicss.scss +17 -0
  115. package/src/layout/_index.scss +3 -0
  116. package/src/layout/container/_index.scss +2 -0
  117. package/src/layout/container/_plugin.scss +26 -0
  118. package/src/layout/container/_variables.scss +5 -0
  119. package/src/layout/flex/_index.scss +2 -0
  120. package/src/layout/flex/_plugin.scss +32 -0
  121. package/src/layout/flex/_variables.scss +9 -0
  122. package/src/layout/grid/_index.scss +2 -0
  123. package/src/layout/grid/_plugin.scss +37 -0
  124. package/src/layout/grid/_variables.scss +37 -0
  125. package/src/module/_index.scss +6 -0
  126. package/src/module/alert/_index.scss +2 -0
  127. package/src/module/alert/_plugin.scss +51 -0
  128. package/src/module/alert/_variables.scss +24 -0
  129. package/src/module/card/_index.scss +2 -0
  130. package/src/module/card/_plugin.scss +31 -0
  131. package/src/module/card/_variables.scss +13 -0
  132. package/src/module/field/_index.scss +2 -0
  133. package/src/module/field/_plugin.scss +49 -0
  134. package/src/module/field/_variables.scss +26 -0
  135. package/src/module/modal/_index.scss +2 -0
  136. package/src/module/modal/_plugin.scss +137 -0
  137. package/src/module/modal/_variables.scss +39 -0
  138. package/src/module/navbar/_index.scss +2 -0
  139. package/src/module/navbar/_plugin.scss +186 -0
  140. package/src/module/navbar/_variables.scss +49 -0
  141. package/src/module/tabs/_index.scss +2 -0
  142. package/src/module/tabs/_plugin.scss +106 -0
  143. package/src/module/tabs/_variables.scss +16 -0
  144. package/src/theme/_create.scss +74 -0
  145. package/src/theme/dark.scss +51 -0
  146. package/src/theme/default.scss +45 -0
  147. package/src/theme/high-contrast.scss +53 -0
  148. package/src/typography/_index.scss +5 -0
  149. package/src/typography/base/_index.scss +2 -0
  150. package/src/typography/base/_plugin.scss +21 -0
  151. package/src/typography/base/_variables.scss +15 -0
  152. package/src/typography/content/_index.scss +2 -0
  153. package/src/typography/content/_plugin.scss +39 -0
  154. package/src/typography/content/_variables.scss +1 -0
  155. package/src/typography/link/_index.scss +2 -0
  156. package/src/typography/link/_mixins.scss +29 -0
  157. package/src/typography/link/_plugin.scss +15 -0
  158. package/src/typography/link/_variables.scss +15 -0
  159. package/src/typography/table/_index.scss +2 -0
  160. package/src/typography/table/_plugin.scss +13 -0
  161. package/src/typography/table/_variables.scss +6 -0
  162. package/src/typography/title/_index.scss +2 -0
  163. package/src/typography/title/_plugin.scss +36 -0
  164. package/src/typography/title/_variables.scss +7 -0
  165. package/src/variables/_color.scss +66 -0
  166. package/src/variables/_default.scss +52 -0
  167. package/src/variables/_feature.scss +39 -0
  168. package/src/variables/_global.scss +93 -0
  169. package/src/variables/_index.scss +7 -0
  170. package/src/variables/_responsive.scss +10 -0
  171. package/src/variables/_selector.scss +1 -0
  172. 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,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -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,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -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,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -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
+ }