@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
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@adnap/krysalicss",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "description": "A modern, extremely modular, multi-theme CSS framework with first-class theming via CSS custom properties.",
6
+ "license": "MIT",
7
+ "author": "adnap",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://gitlab.com/adnap/krysalicss.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://gitlab.com/adnap/krysalicss/-/issues"
14
+ },
15
+ "homepage": "https://krysalicss.eu",
16
+ "main": "dist/krysalicss.css",
17
+ "style": "dist/krysalicss.min.css",
18
+ "sass": "src/krysalicss.scss",
19
+ "unpkg": "dist/krysalicss.min.css",
20
+ "sideEffects": [
21
+ "*.css",
22
+ "*.scss"
23
+ ],
24
+ "exports": {
25
+ ".": {
26
+ "sass": "./src/krysalicss.scss",
27
+ "style": "./dist/krysalicss.min.css",
28
+ "default": "./dist/krysalicss.css"
29
+ },
30
+ "./min": {
31
+ "style": "./dist/krysalicss.min.css",
32
+ "default": "./dist/krysalicss.min.css"
33
+ },
34
+ "./helper/*": {
35
+ "sass": "./src/helper/_*.scss",
36
+ "style": "./dist/helper/*.min.css",
37
+ "default": "./dist/helper/*.css"
38
+ },
39
+ "./*": {
40
+ "sass": "./src/*/_index.scss",
41
+ "style": "./dist/*.min.css",
42
+ "default": "./dist/*.css"
43
+ },
44
+ "./package.json": "./package.json"
45
+ },
46
+ "files": [
47
+ "src",
48
+ "dist",
49
+ "README.md",
50
+ "LICENSE"
51
+ ],
52
+ "keywords": [
53
+ "css",
54
+ "scss",
55
+ "sass",
56
+ "css-framework",
57
+ "framework",
58
+ "modular",
59
+ "responsive",
60
+ "flexbox",
61
+ "themes",
62
+ "dark-mode",
63
+ "css-variables"
64
+ ],
65
+ "devDependencies": {
66
+ "@axe-core/playwright": "^4.11.3",
67
+ "@playwright/test": "^1.59.1",
68
+ "@types/node": "^24.12.2",
69
+ "autoprefixer": "^10.5.0",
70
+ "cssnano": "^8.0.0",
71
+ "postcss": "^8.5.14",
72
+ "sass-embedded": "^1.99.0",
73
+ "tsx": "^4.21.0",
74
+ "typescript": "^6.0.3"
75
+ },
76
+ "engines": {
77
+ "node": ">=24"
78
+ },
79
+ "scripts": {
80
+ "build": "pnpm build:prod && pnpm build:dev",
81
+ "build:prod": "tsx tools/build.ts prod",
82
+ "build:dev": "tsx tools/build.ts dev",
83
+ "watch": "tsx tools/watch.ts",
84
+ "test": "pnpm test:snapshots",
85
+ "test:snapshots": "tsx tools/test-snapshots.ts",
86
+ "test:visual": "pnpm --filter @adnap/krysalicss-docs build && playwright test tests/visual/",
87
+ "test:visual:wipe": "rm -rf tests/visual/__screenshots__",
88
+ "test:visual:update": "pnpm --filter @adnap/krysalicss-docs build && playwright test tests/visual/ --update-snapshots",
89
+ "test:visual:podman": "tsx tools/test-visual-podman.ts",
90
+ "test:visual:podman:update": "tsx tools/test-visual-podman.ts --update",
91
+ "test:a11y": "pnpm --filter @adnap/krysalicss-docs build && playwright test tests/a11y/ --project=a11y",
92
+ "bundle:check": "tsx tools/bundle-size.ts",
93
+ "lint": "deno lint",
94
+ "lint:flags": "tsx tools/lint-feature-flags.ts",
95
+ "lint:motion": "tsx tools/lint-reduced-motion.ts",
96
+ "smoke:imports": "tsx tools/smoke-imports.ts",
97
+ "tokens:build": "tsx tools/export-tokens.ts",
98
+ "format": "deno fmt",
99
+ "format:check": "deno fmt --check",
100
+ "typecheck": "tsc --noEmit",
101
+ "check": "pnpm format:check && pnpm lint && pnpm typecheck",
102
+ "docs:dev": "pnpm --filter @adnap/krysalicss-docs dev",
103
+ "docs:build": "pnpm --filter @adnap/krysalicss-docs build",
104
+ "docs:preview": "pnpm --filter @adnap/krysalicss-docs preview",
105
+ "docs:check": "pnpm --filter @adnap/krysalicss-docs check",
106
+ "build:vue": "pnpm --filter @adnap/krysalicss-vue build",
107
+ "test:vue": "pnpm --filter @adnap/krysalicss-vue test",
108
+ "build:react": "pnpm --filter @adnap/krysalicss-react build",
109
+ "test:react": "pnpm --filter @adnap/krysalicss-react test"
110
+ }
111
+ }
@@ -0,0 +1,18 @@
1
+ // Forwards-only entry. Use this when you want to bring your own theme:
2
+ // @use 'krysalicss/src/index' with ($features: (button, card));
3
+ // @use 'krysalicss/src/theme/dark';
4
+ //
5
+ // Order matters: variables → base → helper → layout → typography → module → element.
6
+ // Themes are NOT included here: consumers pull a theme explicitly.
7
+ //
8
+ // Each category is forwarded with a `<category>-*` prefix so siblings that share
9
+ // generic token names (`$selector`, `$gap`, `$border-radius`) don't collide at
10
+ // this layer. Reach for symbols as `$layout-selector`, `$module-selector`, etc.
11
+ @forward "variables";
12
+ @forward "base" as base-*;
13
+ @forward "helper" as helper-*;
14
+ @forward "layout" as layout-*;
15
+ @forward "typography" as typography-*;
16
+ @forward "module" as module-*;
17
+ @forward "element" as element-*;
18
+ @forward "theme/create" as theme-create-*;
@@ -0,0 +1,2 @@
1
+ @forward "reset";
2
+ @forward "global";
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -0,0 +1,34 @@
1
+ @use "variables" as *;
2
+ @use "../../variables";
3
+
4
+ @if variables.$feature-base-global {
5
+ html {
6
+ @include variables.backgroundColor(bg, $background-fallback);
7
+ @include variables.textColor(fg, $foreground-fallback);
8
+ }
9
+
10
+ // Forced-colors (Windows High Contrast). Authoring tokens remap to OS
11
+ // system colour keywords so user agent contrast wins over brand palette.
12
+ @if $forced-colors-fallback {
13
+ @media (forced-colors: active) {
14
+ :root {
15
+ --kc-bg: Canvas;
16
+ --kc-fg: CanvasText;
17
+ --kc-link: LinkText;
18
+ --kc-link-hover: LinkText;
19
+ --kc-card-bg: Canvas;
20
+ --kc-card-fg: CanvasText;
21
+ --kc-default-bg: Canvas;
22
+ --kc-default-fg: CanvasText;
23
+ --kc-primary-bg: Highlight;
24
+ --kc-primary-fg: HighlightText;
25
+ --kc-warning-bg: Canvas;
26
+ --kc-warning-fg: CanvasText;
27
+ --kc-danger-bg: Canvas;
28
+ --kc-danger-fg: CanvasText;
29
+ --kc-success-bg: Canvas;
30
+ --kc-success-fg: CanvasText;
31
+ }
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,11 @@
1
+ // Fallback values used when the corresponding theme custom property is unset.
2
+ // Match the `white` / `black` palette entries so consumers re-tinting the
3
+ // palette still get coherent fallbacks.
4
+ $background-fallback: #ffffff !default;
5
+ $foreground-fallback: #27282b !default;
6
+
7
+ // Windows High Contrast Mode: opt-in by default. When active, the
8
+ // emitted block remaps theme tokens to `Canvas` / `CanvasText` /
9
+ // `LinkText` / `Highlight` so the OS palette wins. Set to `false` if
10
+ // the consumer would rather author their own forced-colors handling.
11
+ $forced-colors-fallback: true !default;
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -0,0 +1,70 @@
1
+ @use "variables" as *;
2
+ @use "../../variables";
3
+
4
+ @if variables.$feature-base-reset {
5
+ /*! krysalicss-reset
6
+ derived from minireset.css v0.0.6
7
+ MIT
8
+ github.com/jgthms/minireset.css)
9
+ */
10
+
11
+ #{$selector-list} {
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+
16
+ #{$heading-selector} {
17
+ font-size: 100%;
18
+ font-weight: normal;
19
+ }
20
+
21
+ ul {
22
+ list-style: none;
23
+ }
24
+
25
+ #{$form-selector} {
26
+ margin: 0;
27
+ // UA stylesheets apply `font: -webkit-small-control` (or similar) to
28
+ // form controls, which renders ~13px instead of inheriting the page
29
+ // font. Inherit explicitly so `<button class="button">` matches
30
+ // `<a class="button">` and form fields match surrounding prose.
31
+ font-family: inherit;
32
+ font-size: 1em;
33
+ line-height: inherit;
34
+ }
35
+
36
+ html {
37
+ box-sizing: border-box;
38
+ }
39
+
40
+ * {
41
+ &,
42
+ &::before,
43
+ &::after {
44
+ box-sizing: inherit;
45
+ }
46
+ }
47
+
48
+ #{$media-selector} {
49
+ height: auto;
50
+ max-width: 100%;
51
+ }
52
+
53
+ iframe {
54
+ border: 0;
55
+ }
56
+
57
+ table {
58
+ border-collapse: collapse;
59
+ border-spacing: 0;
60
+ }
61
+
62
+ td,
63
+ th {
64
+ padding: 0;
65
+
66
+ &:not([align]) {
67
+ text-align: start;
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,5 @@
1
+ $selector-list: "html, body, p, ol, ul, li, dl, dt, dd, blockquote, figure, fieldset, legend, textarea, pre, iframe, hr, h1, h2, h3, h4, h5, h6"
2
+ !default;
3
+ $heading-selector: "h1, h2, h3, h4, h5, h6" !default;
4
+ $form-selector: "button, input, select, textarea" !default;
5
+ $media-selector: "img, video" !default;
@@ -0,0 +1,11 @@
1
+ @forward "button" as button-*;
2
+ @forward "badge" as badge-*;
3
+ @forward "progress" as progress-*;
4
+ @forward "text-input" as text-input-*;
5
+ @forward "textarea" as textarea-*;
6
+ @forward "select" as select-*;
7
+ @forward "checkbox" as checkbox-*;
8
+ @forward "radio" as radio-*;
9
+ @forward "switch" as switch-*;
10
+ @forward "file" as file-*;
11
+ @forward "range" as range-*;
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -0,0 +1,44 @@
1
+ @use "sass:list";
2
+ @use "variables" as *;
3
+ @use "../../variables";
4
+
5
+ @if variables.$feature-element-badge {
6
+ #{$selector} {
7
+ @include variables.backgroundColor(
8
+ default-bg,
9
+ list.nth($default-combination, 1),
10
+ );
11
+ @include variables.textColor(default-fg, list.nth($default-combination, 2));
12
+ display: inline-flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ padding: 0 $padding-x;
16
+ line-height: $line-height;
17
+ font-size: $font-size;
18
+ border-radius: $border-radius;
19
+ // Subtle outline disambiguates pill shape from page bg when the variant
20
+ // bg ≈ page bg (e.g. .is-default on white, .is-light on dark). Matches
21
+ // the page-foreground axis at low alpha so it reads regardless of
22
+ // variant colour. WCAG 1.4.11 graphical-object contrast.
23
+ outline: 1px solid
24
+ color-mix(in srgb, var(--kc-fg, currentColor) 50%, transparent);
25
+ outline-offset: -1px;
26
+
27
+ &#{$square-selector} {
28
+ border-radius: $square-border-radius;
29
+ }
30
+
31
+ @include variables.combinations using ($label, $tuple) {
32
+ @include variables.backgroundColor(#{$label}-bg, list.nth($tuple, 1));
33
+ @include variables.textColor(#{$label}-fg, list.nth($tuple, 2));
34
+ }
35
+
36
+ // Forced-colors: the 35%-mixed outline collapses to a near-transparent
37
+ // system colour and the pill shape disappears against Canvas. Force a
38
+ // solid CanvasText outline so the boundary stays visible; the variant
39
+ // distinction flattens
40
+ @media (forced-colors: active) {
41
+ outline-color: CanvasText;
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,19 @@
1
+ @use "../../variables";
2
+
3
+ $selector: ".badge" !default;
4
+ $square-selector: "#{variables.$selector-prefix-is}square" !default;
5
+
6
+ // Inline indicator sizing. Fixed line-height keeps the pill height
7
+ // independent of the surrounding text's line-height (which would otherwise
8
+ // stretch a numeric badge inside prose). All three derive from the size
9
+ // scale so the badge sits in the framework's dimensional rhythm.
10
+ $padding-x: variables.$size-extra-small !default;
11
+ $line-height: variables.$size-medium !default;
12
+ $font-size: variables.$size-small !default;
13
+
14
+ // Pill default. The `.is-square` modifier overrides this with the global
15
+ // border radius so badges can match the corner radius of adjacent buttons.
16
+ $border-radius: variables.$global-max-border-radius !default;
17
+ $square-border-radius: variables.$global-border-radius !default;
18
+
19
+ $default-combination: variables.$color-combination-default !default;
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -0,0 +1,173 @@
1
+ @use "sass:list";
2
+ @use "variables" as *;
3
+ @use "../../variables";
4
+ @use "../../helper/controls" as controls;
5
+ @use "../../typography/link/mixins" as link;
6
+
7
+ @if variables.$feature-element-button {
8
+ #{$selector} {
9
+ @include variables.backgroundColor(
10
+ default-bg,
11
+ list.nth($default-combination, 1),
12
+ );
13
+ @include variables.textColor(default-fg, list.nth($default-combination, 2));
14
+ // Transparent border by default so the box stays the same size in
15
+ // every modifier; `.is-outlined` (and its per-combination override
16
+ // below) reach back in and set a visible `border-color`. Without
17
+ // this, toggling outlined would shift the layout by 2px.
18
+ border: 1px solid transparent;
19
+ padding: $padding-y $padding-x;
20
+ min-width: $min-width;
21
+ border-radius: $border-radius;
22
+ cursor: pointer;
23
+ // `<a class="button">` would otherwise inherit the bare-link underline.
24
+ // `.is-link` re-asserts underline via `linkSurface` below.
25
+ text-decoration: none;
26
+
27
+ &:focus-visible {
28
+ @include controls.focus-ring(
29
+ $focus-outline-width,
30
+ $focus-outline-style,
31
+ $focus-outline-offset,
32
+ );
33
+ }
34
+
35
+ // `filter: brightness()` is preferred over swapping `(bg, fg)` on hover:
36
+ // brightness scales every channel uniformly so the rest-state contrast
37
+ // ratio is preserved, which means every shipped combination AND any
38
+ // consumer-supplied palette stay WCAG AA at hover. A bg/fg swap, by
39
+ // contrast, lands at whatever ratio the inverted pair happens to have.
40
+ // Mid-luminance combinations like `.is-primary` and `.is-success` would
41
+ // drop below 4.5:1.
42
+ &:hover {
43
+ filter: brightness($hover-brightness);
44
+ }
45
+
46
+ // Matches the framework convention used by every other interactive
47
+ // element (text-input, select, switch, …): faded glyph + `cursor:
48
+ // not-allowed`, and the brightness hover is unset so the button
49
+ // doesn't visually react under a disabled-pointer.
50
+ &:disabled {
51
+ cursor: not-allowed;
52
+ opacity: $disabled-opacity;
53
+
54
+ &:hover {
55
+ filter: none;
56
+ }
57
+ }
58
+
59
+ @include variables.combinations using ($label, $tuple) {
60
+ @include variables.backgroundColor(#{$label}-bg, list.nth($tuple, 1));
61
+ @include variables.textColor(#{$label}-fg, list.nth($tuple, 2));
62
+
63
+ // `.is-inverted` flips the combination onto the page surface:
64
+ // page bg as the fill, the combination's bg (its "main" colour)
65
+ // as text. Theme-aware via `--kc-bg` / `--kc-<label>-bg` so light
66
+ // theme shows e.g. white-bg + green-text on `is-primary`, dark
67
+ // theme shows dark-bg + themed-green-text. Skipped for `default`
68
+ // because its bg matches the page bg (the result would be
69
+ // invisible); the modifier-less branch below handles that case
70
+ // with a true `(bg, fg)` swap. Border stays at the base rule's
71
+ // transparent placeholder so the surface is borderless.
72
+ @if $label != default {
73
+ &#{$inverted-selector} {
74
+ @include variables.backgroundColor(
75
+ bg,
76
+ list.nth($default-combination, 1),
77
+ );
78
+ @include variables.textColor(#{$label}-bg, list.nth($tuple, 1));
79
+ }
80
+ }
81
+
82
+ // `.is-outlined` keeps the combination's bg as the visible
83
+ // signature: transparent fill, bg-as-text, bg-as-border. Raw
84
+ // literals (not the var-aware mixin) match `.is-inverted`'s
85
+ // legacy-palette stance so contrast stays predictable across
86
+ // consumer-supplied themes. Skipped for `default` because its bg
87
+ // is the page bg in light theme — `.button.is-default.is-outlined`
88
+ // falls through to the modifier-less `&#{$outlined-selector}`
89
+ // rule below + `.button.is-default`'s themed fg.
90
+ @if $label != default {
91
+ &#{$outlined-selector} {
92
+ background: transparent;
93
+ color: list.nth($tuple, 1);
94
+ border: 1px solid list.nth($tuple, 1);
95
+ }
96
+ }
97
+ }
98
+
99
+ // Modifier-less inverted/outlined rules sit AFTER the combinations
100
+ // loop so they out-cascade the `.is-default` combination at equal
101
+ // specificity (both are two-class selectors). Higher-specificity
102
+ // `.is-<combo>.is-<modifier>` rules in the loop above still win for
103
+ // explicit combinations.
104
+ //
105
+ // `.button.is-inverted` (and `.is-default.is-inverted`) is a true
106
+ // `(bg, fg)` swap of the default combination's theme tokens —
107
+ // `--kc-default-fg` becomes the fill, `--kc-default-bg` becomes
108
+ // the text. The non-default combinations follow a different rule
109
+ // (page-bg fill + combo's bg as text); default needs the explicit
110
+ // swap because its bg matches the page bg, so a page-bg-fill
111
+ // inversion would render invisible. Border stays transparent
112
+ // (inherited from the base rule).
113
+ &#{$inverted-selector} {
114
+ @include variables.backgroundColor(
115
+ default-fg,
116
+ list.nth($default-combination, 2),
117
+ );
118
+ @include variables.textColor(
119
+ default-bg,
120
+ list.nth($default-combination, 1),
121
+ );
122
+ }
123
+
124
+ // `.button.is-outlined` strips the fill and re-asserts a visible
125
+ // themed border. The default button's visible signature is its
126
+ // themed fg, so the border tracks `--kc-default-fg` here; using
127
+ // `--kc-default-bg` for the outline would render invisible against
128
+ // the page in light theme. Per-combination outlined rules above
129
+ // override `border-color` with the combo's bg.
130
+ &#{$outlined-selector} {
131
+ background: transparent;
132
+ @include variables.defaultProperty(
133
+ border-color,
134
+ default-fg,
135
+ list.nth($default-combination, 2),
136
+ );
137
+ }
138
+
139
+ // `.button.is-link` styles the button as a link: no fill, no
140
+ // visible border (kept transparent so layout is preserved when
141
+ // toggling), themed `--kc-link` text, and `--kc-link-hover` on
142
+ // hover. The brightness filter is unset so hover transitions
143
+ // through the link-hover token rather than darkening the
144
+ // surrounding chrome. Sits after the combinations loop so it
145
+ // wins over the combination's bg/fg/border at equal specificity
146
+ // when authors compose `.is-<combo>.is-link` (the link surface
147
+ // takes priority — combinations have no link-styled variant).
148
+ &#{$link-selector} {
149
+ background: transparent;
150
+ border: 1px solid transparent;
151
+ @include link.linkSurface(
152
+ $fallback: list.nth($default-combination, 2),
153
+ $hover-fallback: list.nth($default-combination, 2),
154
+ );
155
+
156
+ &:hover {
157
+ filter: none;
158
+ }
159
+ }
160
+
161
+ // Forced-colors (Windows High Contrast). The `filter: brightness()` hover
162
+ // is a no-op once colours are system-forced, so press feedback would
163
+ // disappear. Replace with an explicit Highlight outline on hover; the
164
+ // focus-visible ring already handles keyboard focus via system colours
165
+ // (UA forces outline-color to CanvasText). WCAG 1.4.11 / 2.4.7.
166
+ @media (forced-colors: active) {
167
+ &:hover {
168
+ outline: 2px solid Highlight;
169
+ outline-offset: 2px;
170
+ }
171
+ }
172
+ }
173
+ }
@@ -0,0 +1,36 @@
1
+ @use "sass:math";
2
+ @use "../../variables";
3
+
4
+ $selector: "button, .button" !default;
5
+ $inverted-selector: "#{variables.$selector-prefix-is}inverted" !default;
6
+ $outlined-selector: "#{variables.$selector-prefix-is}outlined" !default;
7
+ $link-selector: "#{variables.$selector-prefix-is}link" !default;
8
+
9
+ @function _touch-target-padding($target, $line-height-rem) {
10
+ @return math.max(0rem, math.div($target - $line-height-rem, 2));
11
+ }
12
+
13
+ // Vertical padding is derived so the AAA touch-target invariant is enforced
14
+ // in code, not just a comment. The button has no explicit `font-size`, so it
15
+ // inherits the body size (`1rem` once `$global-base-size` is applied at the
16
+ // root). Effective height = padding-y × 2 + line-height × font-size; setting
17
+ // padding-y to half the slack between the touch-target floor and the
18
+ // rendered line height gives exactly the AAA minimum at default settings:
19
+ // (2.75rem − 1rem × 1.5) / 2 = 0.625rem. Override `$global-min-touch-target`
20
+ // or `$global-body-line-height` and the padding follows.
21
+ $padding-y: _touch-target-padding(
22
+ variables.$global-min-touch-target,
23
+ 1rem * variables.$global-body-line-height
24
+ ) !default;
25
+ $padding-x: variables.$size-normal !default;
26
+
27
+ $min-width: variables.$global-min-touch-target !default;
28
+ $border-radius: variables.$global-border-radius !default;
29
+ $default-combination: variables.$color-combination-default !default;
30
+
31
+ $focus-outline-width: variables.$global-focus-outline-width !default;
32
+ $focus-outline-style: variables.$global-focus-outline-style !default;
33
+ $focus-outline-offset: variables.$global-focus-outline-offset !default;
34
+
35
+ $hover-brightness: variables.$global-hover-brightness !default;
36
+ $disabled-opacity: variables.$global-disabled-opacity !default;
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";
@@ -0,0 +1,48 @@
1
+ @use "variables" as *;
2
+ @use "../../variables";
3
+ @use "../../helper/controls" as controls;
4
+
5
+ @if variables.$feature-element-checkbox {
6
+ #{$selector} {
7
+ appearance: none;
8
+ -webkit-appearance: none;
9
+ -moz-appearance: none;
10
+ display: inline-block;
11
+ width: $size;
12
+ height: $size;
13
+ margin: 0;
14
+ padding: 0;
15
+ @include controls.borderWithMix($border-width, $border-mix);
16
+ border-radius: $border-radius;
17
+ background-color: transparent;
18
+ vertical-align: middle;
19
+ flex-shrink: 0;
20
+
21
+ &:checked {
22
+ background-color: var(--kc-primary-bg, currentColor);
23
+ border-color: var(--kc-primary-bg, currentColor);
24
+ // The SVG strokes with `currentColor`; pinning `color` to the
25
+ // primary foreground token guarantees the tick contrasts the fill
26
+ // whatever the theme's primary palette is.
27
+ color: var(--kc-primary-fg, currentColor);
28
+ background-image: $check-image;
29
+ background-repeat: no-repeat;
30
+ background-position: center;
31
+ background-size: 80% auto;
32
+ }
33
+
34
+ &:focus-visible {
35
+ @include controls.focus-ring(
36
+ $focus-outline-width,
37
+ $focus-outline-style,
38
+ $focus-outline-offset,
39
+ );
40
+ }
41
+
42
+ @include controls.invalidControl();
43
+
44
+ &:disabled {
45
+ @include controls.disabledControl($disabled-opacity);
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,24 @@
1
+ @use "../../variables";
2
+
3
+ // Excludes `.switch`-classed inputs so the switch plugin can fully restyle
4
+ // them. Keep this exclusion in sync if you add another checkbox variant.
5
+ $selector: 'input[type="checkbox"]:not(.switch)' !default;
6
+
7
+ $size: 1rem !default;
8
+ $border-radius: 3px !default;
9
+ $border-width: variables.$global-border-width !default;
10
+
11
+ $focus-outline-width: variables.$global-focus-outline-width !default;
12
+ $focus-outline-style: variables.$global-focus-outline-style !default;
13
+ $focus-outline-offset: variables.$global-focus-outline-offset !default;
14
+
15
+ $disabled-opacity: variables.$global-disabled-opacity !default;
16
+ $border-mix: variables.$global-border-mix !default;
17
+
18
+ // Inline tick mark using `stroke="currentColor"`: the SVG inherits the
19
+ // element's `color`, which the plugin pins to `--kc-primary-fg` on
20
+ // `:checked`. That keeps the tick AAA-readable against any primary-bg
21
+ // the theme picks (deep sage, mid-tone teal, a high-contrast cyan):
22
+ // the fg token is always the contrast pair of the bg token.
23
+ $check-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none"><path d="M2.5 6.5l2.5 2.5 4.5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>')
24
+ !default;
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "plugin";