klods-css 1.9.0 → 1.11.0

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/dist/klods.css CHANGED
@@ -94,6 +94,11 @@
94
94
  --klods-gutter: var(--klods-space-5);
95
95
  --klods-transition: 150ms ease;
96
96
  }
97
+ @media (max-width: 48rem) {
98
+ :root {
99
+ --klods-gutter: var(--klods-space-4);
100
+ }
101
+ }
97
102
  }
98
103
  @layer klods.themes {
99
104
  [data-theme=dark] {
@@ -216,12 +221,63 @@
216
221
  .klods-page--with-sidebar,
217
222
  .klods-page--with-sidebar.klods-page--sidebar-trailing {
218
223
  grid-template-columns: 1fr;
219
- grid-template-areas: "header" "sidebar" "content" "footer";
224
+ grid-template-areas: "header" "content" "footer";
225
+ }
226
+ .klods-page--with-sidebar > .klods-sidebar {
227
+ display: none;
228
+ position: fixed;
229
+ inset-block: 0;
230
+ inset-inline-start: 0;
231
+ width: min(18rem, 85vw);
232
+ z-index: 7;
233
+ border-right: 1px solid var(--klods-color-border);
234
+ border-bottom: none;
235
+ overflow-y: auto;
236
+ box-shadow: var(--klods-shadow-lg);
237
+ }
238
+ .klods-page[data-sidebar-open] > .klods-sidebar {
239
+ display: block;
240
+ }
241
+ .klods-page[data-sidebar-open]::before {
242
+ content: "";
243
+ position: fixed;
244
+ inset: 0;
245
+ z-index: 5;
246
+ background: rgba(0, 0, 0, 0.4);
247
+ }
248
+ .klods-page--with-sidebar > .klods-header {
249
+ isolation: isolate;
250
+ z-index: 6;
251
+ }
252
+ .klods-page--with-sidebar > .klods-sidebar > * {
253
+ position: static;
254
+ max-height: none;
255
+ overflow-y: visible;
220
256
  }
221
- .klods-sidebar {
222
- border-right: none;
223
- border-left: none;
224
- border-bottom: 1px solid var(--klods-color-border);
257
+ }
258
+ .klods-sidebar-toggle {
259
+ display: none;
260
+ align-items: center;
261
+ justify-content: center;
262
+ padding: var(--klods-space-2);
263
+ border-radius: var(--klods-radius-sm);
264
+ background: transparent;
265
+ border: 1px solid transparent;
266
+ color: inherit;
267
+ cursor: pointer;
268
+ line-height: 1;
269
+ transition: background var(--klods-transition);
270
+ }
271
+ .klods-sidebar-toggle:hover, .klods-sidebar-toggle:focus-visible {
272
+ background: var(--klods-color-surface-2);
273
+ }
274
+ .klods-sidebar-toggle:focus-visible {
275
+ outline: 2px solid var(--klods-color-accent);
276
+ outline-offset: 2px;
277
+ }
278
+ @media (max-width: 48rem) {
279
+ .klods-sidebar-toggle {
280
+ display: inline-flex;
225
281
  }
226
282
  }
227
283
  .klods-section {
@@ -265,6 +321,45 @@
265
321
  background: var(--klods-color-accent);
266
322
  color: var(--klods-color-accent-fg);
267
323
  }
324
+ .klods-nav__toggle {
325
+ display: none;
326
+ align-items: center;
327
+ justify-content: center;
328
+ padding: var(--klods-space-2);
329
+ border-radius: var(--klods-radius-sm);
330
+ background: transparent;
331
+ border: 1px solid transparent;
332
+ color: inherit;
333
+ cursor: pointer;
334
+ line-height: 1;
335
+ transition: background var(--klods-transition);
336
+ }
337
+ .klods-nav__toggle:hover, .klods-nav__toggle:focus-visible {
338
+ background: var(--klods-color-surface-2);
339
+ }
340
+ .klods-nav__toggle:focus-visible {
341
+ outline: 2px solid var(--klods-color-accent);
342
+ outline-offset: 2px;
343
+ }
344
+ @media (max-width: 48rem) {
345
+ .klods-nav--collapse {
346
+ flex-wrap: wrap;
347
+ }
348
+ .klods-nav--collapse .klods-nav__toggle {
349
+ display: inline-flex;
350
+ }
351
+ .klods-nav--collapse .klods-nav__list {
352
+ display: none;
353
+ width: 100%;
354
+ flex-direction: column;
355
+ align-items: stretch;
356
+ gap: var(--klods-space-1);
357
+ padding-block: var(--klods-space-2);
358
+ }
359
+ .klods-nav--collapse[data-nav-open] .klods-nav__list {
360
+ display: flex;
361
+ }
362
+ }
268
363
  .klods-toc {
269
364
  list-style: none;
270
365
  padding: 0;
@@ -486,6 +581,10 @@
486
581
  background: transparent;
487
582
  font-size: inherit;
488
583
  }
584
+ .klods-table-wrap {
585
+ overflow-x: auto;
586
+ -webkit-overflow-scrolling: touch;
587
+ }
489
588
  .klods-table {
490
589
  width: 100%;
491
590
  border-collapse: collapse;
@@ -768,6 +867,16 @@
768
867
  flex-direction: row-reverse;
769
868
  justify-content: space-between;
770
869
  }
870
+ @media (max-width: 48rem) {
871
+ .klods-input,
872
+ .klods-select {
873
+ min-height: 2.75rem;
874
+ }
875
+ .klods-checkbox,
876
+ .klods-radio {
877
+ min-height: 2.75rem;
878
+ }
879
+ }
771
880
  }
772
881
  @layer klods.utilities {
773
882
  .klods-stack {
@@ -931,4 +1040,27 @@
931
1040
  white-space: nowrap;
932
1041
  border: 0;
933
1042
  }
1043
+ @media (max-width: 48rem) {
1044
+ .klods-grid--cols-3,
1045
+ .klods-grid--cols-4,
1046
+ .klods-grid--cols-5,
1047
+ .klods-grid--cols-6 {
1048
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1049
+ }
1050
+ }
1051
+ @media (max-width: 30rem) {
1052
+ [class*=klods-grid--cols-] {
1053
+ grid-template-columns: 1fr;
1054
+ }
1055
+ }
1056
+ @media (max-width: 30rem) {
1057
+ .klods-hide-mobile {
1058
+ display: none !important;
1059
+ }
1060
+ }
1061
+ @media (max-width: 48rem) {
1062
+ .klods-hide-tablet {
1063
+ display: none !important;
1064
+ }
1065
+ }
934
1066
  }
package/dist/klods.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * klods.js — interactive helpers for vanilla HTML pages.
3
+ *
4
+ * A tiny companion to klods-css that wires up sidebar and nav toggling
5
+ * without requiring a build step or klods-js.
6
+ *
7
+ * Usage:
8
+ * <script src="https://unpkg.com/klods-css/dist/klods.js"></script>
9
+ *
10
+ * The helpers are exposed on `window.klods`:
11
+ * klods.toggleSidebar(buttonEl)
12
+ * klods.toggleNav(buttonEl)
13
+ */
14
+ (function (global) {
15
+ "use strict";
16
+
17
+ /**
18
+ * Toggle the open/closed state of the sidebar drawer.
19
+ * Pass the toggle button element (or any element inside .klods-page).
20
+ *
21
+ * On first call per page element, auto-wires:
22
+ * - Clicking a link inside the sidebar closes the drawer.
23
+ * - Clicking the backdrop (outside sidebar and header) closes the drawer.
24
+ */
25
+ function toggleSidebar(targetEl) {
26
+ var pageEl = targetEl.closest(".klods-page");
27
+ if (!pageEl) return;
28
+
29
+ if (!pageEl.hasAttribute("data-sidebar-wired")) {
30
+ pageEl.setAttribute("data-sidebar-wired", "");
31
+ var sidebarEl = pageEl.querySelector(":scope > .klods-sidebar");
32
+ if (sidebarEl) {
33
+ sidebarEl.addEventListener("click", function (e) {
34
+ e.stopPropagation();
35
+ });
36
+ sidebarEl.addEventListener("click", function (e) {
37
+ if (e.target.closest("a")) pageEl.removeAttribute("data-sidebar-open");
38
+ });
39
+ }
40
+ pageEl.addEventListener("click", function (e) {
41
+ if (!pageEl.hasAttribute("data-sidebar-open")) return;
42
+ var headerEl = pageEl.querySelector(":scope > .klods-header");
43
+ if (headerEl && headerEl.contains(e.target)) return;
44
+ pageEl.removeAttribute("data-sidebar-open");
45
+ });
46
+ }
47
+
48
+ if (pageEl.hasAttribute("data-sidebar-open")) {
49
+ pageEl.removeAttribute("data-sidebar-open");
50
+ } else {
51
+ pageEl.setAttribute("data-sidebar-open", "");
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Toggle the open/closed state of a .klods-nav--collapse element.
57
+ * Pass the toggle button element (or any element inside the nav).
58
+ */
59
+ function toggleNav(targetEl) {
60
+ var navEl = targetEl.closest(".klods-nav--collapse");
61
+ if (!navEl) return;
62
+ if (navEl.hasAttribute("data-nav-open")) {
63
+ navEl.removeAttribute("data-nav-open");
64
+ } else {
65
+ navEl.setAttribute("data-nav-open", "");
66
+ }
67
+ }
68
+
69
+ global.klods = { toggleSidebar: toggleSidebar, toggleNav: toggleNav };
70
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : this);
@@ -1 +1 @@
1
- @layer klods.reset, klods.tokens, klods.themes, klods.layout, klods.components, klods.utilities;@layer klods.reset{*,*::before,*::after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;text-size-adjust:100%}body{margin:0;min-height:100dvh;font-family:var(--klods-font-sans);font-size:var(--klods-font-size-base);line-height:var(--klods-line-height-base);color:var(--klods-color-fg);background:var(--klods-color-bg)}img,picture,svg,video{display:block;max-width:100%}pre,code{font-family:var(--klods-font-mono)}pre{overflow-x:auto;white-space:pre-wrap;overflow-wrap:break-word}button,input,select,textarea{font:inherit;color:inherit}a{color:var(--klods-color-link)}}@layer klods.tokens{:root{--klods-color-bg: #ffffff;--klods-color-fg: #1a1a1a;--klods-color-muted: #6b7280;--klods-color-surface: #f5f5f7;--klods-color-surface-2: #ececef;--klods-color-border: #e5e7eb;--klods-color-accent: #5b5bd6;--klods-color-accent-fg: #ffffff;--klods-color-link: var(--klods-color-accent);--klods-color-danger: #d1335b;--klods-color-success: #2f9e44;--klods-color-warning: #d97706;--klods-color-info: #2563eb;--klods-font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--klods-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--klods-font-size-base: 16px;--klods-font-size-sm: 0.875rem;--klods-font-size-lg: 1.125rem;--klods-font-size-xl: 1.5rem;--klods-font-size-2xl: 2rem;--klods-font-size-3xl: 2.5rem;--klods-line-height-base: 1.5;--klods-line-height-tight: 1.2;--klods-space-0: 0;--klods-space-1: 0.25rem;--klods-space-2: 0.5rem;--klods-space-3: 0.75rem;--klods-space-4: 1rem;--klods-space-5: 1.5rem;--klods-space-6: 2rem;--klods-space-7: 3rem;--klods-space-8: 4rem;--klods-radius-sm: 4px;--klods-radius-md: 8px;--klods-radius-lg: 16px;--klods-radius-pill: 999px;--klods-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);--klods-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);--klods-shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.12);--klods-content-max: 72rem;--klods-content-narrow: 48rem;--klods-sidebar-width: 16rem;--klods-header-height: 4rem;--klods-footer-height: auto;--klods-gutter: var(--klods-space-5);--klods-transition: 150ms ease}}@layer klods.themes{[data-theme=dark]{--klods-color-bg: #0f1115;--klods-color-fg: #ececef;--klods-color-muted: #9aa0aa;--klods-color-surface: #181b22;--klods-color-surface-2: #1f232c;--klods-color-border: #2a2f3a;--klods-color-accent: #8a8aff;--klods-color-accent-fg: #0f1115}[data-theme=playful]{--klods-color-bg: #fff7fb;--klods-color-fg: #2a1a3a;--klods-color-surface: #ffe8f3;--klods-color-surface-2: #ffd6ea;--klods-color-border: #f4bfd9;--klods-color-accent: #e63990;--klods-color-accent-fg: #ffffff;--klods-radius-sm: 8px;--klods-radius-md: 16px;--klods-radius-lg: 28px;--klods-font-sans: "Comic Sans MS", "Comic Sans", ui-rounded, system-ui, sans-serif}[data-theme=brutalist]{--klods-color-bg: #ffffff;--klods-color-fg: #000000;--klods-color-surface: #ffffff;--klods-color-surface-2: #f0f0f0;--klods-color-border: #000000;--klods-color-accent: #ffe600;--klods-color-accent-fg: #000000;--klods-color-link: #000000;--klods-radius-sm: 0;--klods-radius-md: 0;--klods-radius-lg: 0;--klods-radius-pill: 0;--klods-shadow-sm: 4px 4px 0 #000;--klods-shadow-md: 6px 6px 0 #000;--klods-shadow-lg: 10px 10px 0 #000;--klods-font-sans: "Courier New", ui-monospace, monospace}}@layer klods.layout{.klods-page{display:grid;min-height:100dvh;grid-template-columns:1fr;grid-template-rows:auto 1fr auto;grid-template-areas:"header" "content" "footer"}.klods-page--with-sidebar{grid-template-columns:var(--klods-sidebar-width) 1fr;grid-template-areas:"header header" "sidebar content" "footer footer"}.klods-page--with-sidebar.klods-page--sidebar-trailing{grid-template-columns:1fr var(--klods-sidebar-width);grid-template-areas:"header header" "content sidebar" "footer footer"}.klods-header{grid-area:header;display:flex;align-items:center;gap:var(--klods-space-4);padding:0 var(--klods-gutter);min-height:var(--klods-header-height);background:var(--klods-color-surface);border-bottom:1px solid var(--klods-color-border)}.klods-page--sticky-header .klods-header{position:sticky;top:0;z-index:10}html:has(.klods-page--sticky-header){scroll-padding-top:var(--klods-header-height)}.klods-sidebar{grid-area:sidebar;padding:var(--klods-gutter);background:var(--klods-color-surface);border-right:1px solid var(--klods-color-border)}.klods-sidebar>*{position:sticky;top:var(--klods-gutter);max-height:calc(100dvh - var(--klods-gutter)*2);overflow-y:auto}.klods-page--sticky-header .klods-sidebar>*{top:calc(var(--klods-header-height) + var(--klods-gutter));max-height:calc(100dvh - var(--klods-header-height) - var(--klods-gutter)*2)}.klods-page--sidebar-trailing .klods-sidebar{border-right:none;border-left:1px solid var(--klods-color-border)}.klods-content{grid-area:content;padding:var(--klods-gutter);min-width:0;max-width:var(--klods-content-max);margin-inline:auto;width:100%}.klods-content--narrow{max-width:var(--klods-content-narrow);margin-inline:auto;width:100%}.klods-footer{grid-area:footer;padding:var(--klods-gutter);background:var(--klods-color-surface);border-top:1px solid var(--klods-color-border);color:var(--klods-color-muted)}@media(max-width: 48rem){.klods-page--with-sidebar,.klods-page--with-sidebar.klods-page--sidebar-trailing{grid-template-columns:1fr;grid-template-areas:"header" "sidebar" "content" "footer"}.klods-sidebar{border-right:none;border-left:none;border-bottom:1px solid var(--klods-color-border)}}.klods-section{scroll-margin-top:var(--klods-space-5)}.klods-section+.klods-section{margin-block-start:var(--klods-space-7);padding-block-start:var(--klods-space-6);border-block-start:1px solid var(--klods-color-border)}}@layer klods.components{.klods-nav{display:flex;align-items:center;gap:var(--klods-space-4)}.klods-nav__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--klods-space-3);list-style:none;padding:0;margin:0}.klods-nav__link{display:inline-flex;align-items:center;padding:var(--klods-space-2) var(--klods-space-3);border-radius:var(--klods-radius-sm);color:inherit;text-decoration:none;transition:background var(--klods-transition)}.klods-nav__link:hover,.klods-nav__link:focus-visible{background:var(--klods-color-surface-2)}.klods-nav__link--active{background:var(--klods-color-accent);color:var(--klods-color-accent-fg)}.klods-toc{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--klods-space-1)}.klods-toc a{display:block;padding:var(--klods-space-2) var(--klods-space-3);border-radius:var(--klods-radius-sm);color:inherit;text-decoration:none;transition:background var(--klods-transition)}.klods-toc a:hover{background:var(--klods-color-surface-2)}.klods-toc--sub{padding-inline-start:var(--klods-space-3);margin-top:var(--klods-space-1)}.klods-toc--sub a{font-size:var(--klods-font-size-sm);color:var(--klods-color-muted);padding:var(--klods-space-1) var(--klods-space-3)}.klods-toc--sub a:hover{color:var(--klods-color-fg)}.klods-card{display:flex;flex-direction:column;gap:var(--klods-space-3);padding:var(--klods-space-5);background:var(--klods-color-surface);border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-md)}.klods-card--elevated{border:none;box-shadow:var(--klods-shadow-md)}.klods-card__title{margin:0;font-size:var(--klods-font-size-lg);line-height:var(--klods-line-height-tight)}.klods-card__body{margin:0}.klods-card__footer{display:flex;gap:var(--klods-space-3);padding-top:var(--klods-space-3);border-top:1px solid var(--klods-color-border)}.klods-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--klods-space-2);padding:var(--klods-space-2) var(--klods-space-4);font:inherit;font-weight:600;color:var(--klods-color-fg);background:var(--klods-color-surface-2);border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-sm);cursor:pointer;transition:background var(--klods-transition),filter var(--klods-transition),transform var(--klods-transition)}.klods-button:hover{filter:brightness(0.95)}.klods-button:active{transform:translateY(1px)}.klods-button--primary{background:var(--klods-color-accent);color:var(--klods-color-accent-fg);border-color:rgba(0,0,0,0)}.klods-button--primary:hover{filter:brightness(0.95);background:var(--klods-color-accent)}.klods-button--danger{background:var(--klods-color-danger);color:#fff;border-color:rgba(0,0,0,0)}.klods-button--danger:hover{filter:brightness(0.9);background:var(--klods-color-danger)}.klods-button--ghost{background:rgba(0,0,0,0);border-color:rgba(0,0,0,0)}.klods-button--ghost:hover{background:var(--klods-color-surface-2);filter:none}.klods-button-group{display:inline-flex;border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-pill)}.klods-button-group>.klods-button{border-radius:0;border-color:rgba(0,0,0,0);position:relative}.klods-button-group>.klods-button:not(:last-child){border-inline-end:1px solid var(--klods-color-border)}.klods-button-group>.klods-button:first-child{border-start-start-radius:var(--klods-radius-pill);border-end-start-radius:var(--klods-radius-pill)}.klods-button-group>.klods-button:last-child{border-start-end-radius:var(--klods-radius-pill);border-end-end-radius:var(--klods-radius-pill)}.klods-button-group>.klods-button:hover,.klods-button-group>.klods-button:focus-visible{z-index:1}.klods-button-group>.klods-button[aria-pressed=true]{background:var(--klods-color-accent);color:var(--klods-color-accent-fg);border-color:var(--klods-color-accent);z-index:1}.klods-badge{display:inline-flex;align-items:center;padding:.125rem var(--klods-space-2);font-size:var(--klods-font-size-sm);font-weight:600;color:var(--klods-color-fg);background:var(--klods-color-surface-2);border-radius:var(--klods-radius-pill)}.klods-badge--accent{background:var(--klods-color-accent);color:var(--klods-color-accent-fg)}.klods-badge--success{background:var(--klods-color-success);color:#fff}.klods-badge--danger{background:var(--klods-color-danger);color:#fff}.klods-alert{padding:var(--klods-space-4);border-radius:var(--klods-radius-md);border:1px solid var(--klods-color-border);background:var(--klods-color-surface)}.klods-alert--info{border-color:var(--klods-color-info);background:color-mix(in srgb, var(--klods-color-info) 12%, var(--klods-color-surface))}.klods-alert--success{border-color:var(--klods-color-success);background:color-mix(in srgb, var(--klods-color-success) 12%, var(--klods-color-surface))}.klods-alert--warning{border-color:var(--klods-color-warning);background:color-mix(in srgb, var(--klods-color-warning) 12%, var(--klods-color-surface))}.klods-alert--danger{border-color:var(--klods-color-danger);background:color-mix(in srgb, var(--klods-color-danger) 12%, var(--klods-color-surface))}.klods-prose>*+*{margin-block-start:var(--klods-space-4)}.klods-prose h1,.klods-prose h2,.klods-prose h3{line-height:var(--klods-line-height-tight)}.klods-prose code{font-family:var(--klods-font-mono);font-size:.95em;padding:.1em .35em;background:var(--klods-color-surface-2);border-radius:var(--klods-radius-sm)}.klods-prose pre{font-family:var(--klods-font-mono);padding:var(--klods-space-4);overflow-x:auto;background:var(--klods-color-surface-2);border-radius:var(--klods-radius-md)}.klods-prose pre code{padding:0;background:rgba(0,0,0,0)}code{font-size:.9em;padding:.1em .35em;background:var(--klods-color-surface-2);border-radius:var(--klods-radius-sm)}pre{padding:var(--klods-space-4);background:var(--klods-color-surface-2);border-radius:var(--klods-radius-md)}pre code{padding:0;background:rgba(0,0,0,0);font-size:inherit}.klods-table{width:100%;border-collapse:collapse}.klods-table th,.klods-table td{padding:var(--klods-space-3) var(--klods-space-4);text-align:left;border-bottom:1px solid var(--klods-color-border)}.klods-table th{font-weight:600;background:var(--klods-color-surface)}.klods-table--striped tbody tr:nth-child(even) td{background:var(--klods-color-surface)}.klods-table--dense th,.klods-table--dense td{padding:var(--klods-space-2) var(--klods-space-3)}.klods-box{padding:var(--klods-space-3);background:var(--klods-color-surface-2);border-radius:var(--klods-radius-sm);text-align:center}.klods-muted{color:var(--klods-color-muted)}.klods-text-center{text-align:center}.klods-lead{font-size:var(--klods-font-size-lg);color:var(--klods-color-muted)}.klods-form{display:flex;flex-direction:column;gap:var(--klods-space-5)}.klods-field{display:flex;flex-direction:column;gap:var(--klods-space-2)}.klods-label{font-size:var(--klods-font-size-sm);font-weight:600;color:var(--klods-color-fg)}.klods-label--required::after{content:" *";color:var(--klods-color-danger);font-weight:400}.klods-input,.klods-select,.klods-textarea{display:block;width:100%;padding:var(--klods-space-2) var(--klods-space-3);font:inherit;color:var(--klods-color-fg);background:var(--klods-color-bg);border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-sm);transition:border-color var(--klods-transition),box-shadow var(--klods-transition);appearance:none}.klods-input::placeholder,.klods-textarea::placeholder{color:var(--klods-color-muted)}.klods-input:hover,.klods-select:hover,.klods-textarea:hover{border-color:var(--klods-color-muted)}.klods-input:focus,.klods-select:focus,.klods-textarea:focus{outline:none;border-color:var(--klods-color-accent);box-shadow:0 0 0 3px color-mix(in srgb, var(--klods-color-accent) 20%, transparent)}.klods-input:disabled,.klods-select:disabled,.klods-textarea:disabled{opacity:.5;cursor:not-allowed;background:var(--klods-color-surface-2)}.klods-input--sm{padding:var(--klods-space-1) var(--klods-space-2);font-size:var(--klods-font-size-sm)}.klods-input--lg{padding:var(--klods-space-3) var(--klods-space-4);font-size:var(--klods-font-size-lg)}.klods-input--range{display:flex;align-items:center;gap:var(--klods-space-3);cursor:default}.klods-input--range input{flex:1 1 auto;padding:0;border:none;background:rgba(0,0,0,0);appearance:auto;accent-color:var(--klods-color-accent)}.klods-input--range output{min-width:3ch;text-align:right;font-variant-numeric:tabular-nums}.klods-input--color{display:flex;align-items:center;gap:var(--klods-space-3)}.klods-input--color input{width:2rem;height:1.5rem;padding:0;border:0;background:none;appearance:auto;cursor:pointer}.klods-select-wrapper{position:relative;display:block}.klods-select-wrapper::after{content:"";position:absolute;inset-inline-end:var(--klods-space-3);top:50%;transform:translateY(-50%);width:1rem;height:1rem;background-color:var(--klods-color-muted);mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m4 6 4 4 4-4'/%3E%3C/svg%3E");mask-repeat:no-repeat;mask-size:contain;pointer-events:none}.klods-select-wrapper:has(.klods-select:disabled)::after{opacity:.5}.klods-select{padding-inline-end:calc(var(--klods-space-3)*2 + 1rem)}.klods-textarea{resize:vertical;min-height:7rem}.klods-help{font-size:var(--klods-font-size-sm);color:var(--klods-color-muted);margin-top:calc(var(--klods-space-1) - var(--klods-space-2))}.klods-error{font-size:var(--klods-font-size-sm);color:var(--klods-color-danger);display:none;margin-top:calc(var(--klods-space-1) - var(--klods-space-2))}.klods-field--invalid .klods-input,.klods-field--invalid .klods-select,.klods-field--invalid .klods-textarea{border-color:var(--klods-color-danger)}.klods-field--invalid .klods-input:focus,.klods-field--invalid .klods-select:focus,.klods-field--invalid .klods-textarea:focus{box-shadow:0 0 0 3px color-mix(in srgb, var(--klods-color-danger) 20%, transparent)}.klods-field--invalid .klods-error{display:block}.klods-field--invalid .klods-help{display:none}.klods-checkbox,.klods-radio{display:inline-flex;align-items:center;gap:var(--klods-space-2);cursor:pointer;user-select:none}.klods-checkbox input[type=checkbox],.klods-checkbox input[type=radio],.klods-radio input[type=checkbox],.klods-radio input[type=radio]{accent-color:var(--klods-color-accent);width:1rem;height:1rem;margin:0;flex-shrink:0;cursor:pointer}.klods-checkbox:has(input:disabled),.klods-radio:has(input:disabled){opacity:.5;cursor:not-allowed}.klods-checkbox:has(input:disabled) input,.klods-radio:has(input:disabled) input{cursor:not-allowed}.klods-switch{position:relative;display:inline-flex;align-items:center;gap:var(--klods-space-3);cursor:pointer;user-select:none}.klods-switch:has(.klods-switch__input:disabled){opacity:.5;cursor:not-allowed}.klods-switch__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.klods-switch__track{position:relative;display:inline-block;flex-shrink:0;width:2.5rem;height:1.5rem;background:var(--klods-color-border);border-radius:var(--klods-radius-pill);transition:background var(--klods-transition)}.klods-switch__track::after{content:"";position:absolute;top:.1875rem;left:.1875rem;width:1.125rem;height:1.125rem;background:var(--klods-color-bg);border-radius:50%;box-shadow:var(--klods-shadow-sm);transition:transform var(--klods-transition)}.klods-switch__input:checked+.klods-switch__track{background:var(--klods-color-accent)}.klods-switch__input:checked+.klods-switch__track::after{transform:translateX(1rem)}.klods-switch__input:focus-visible+.klods-switch__track{outline:2px solid var(--klods-color-accent);outline-offset:2px}.klods-switch__label{line-height:var(--klods-line-height-tight)}.klods-switch--reverse{display:flex;width:100%;flex-direction:row-reverse;justify-content:space-between}}@layer klods.utilities{.klods-stack{display:flex;flex-direction:column;gap:var(--klods-space-4)}.klods-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--klods-space-4)}.klods-row{display:flex;flex-direction:row;align-items:center;gap:var(--klods-space-4)}.klods-grid{display:grid;gap:var(--klods-space-4);grid-template-columns:repeat(2, minmax(0, 1fr))}.klods-grid--cols-1{grid-template-columns:repeat(1, minmax(0, 1fr))}.klods-grid--cols-2{grid-template-columns:repeat(2, minmax(0, 1fr))}.klods-grid--cols-3{grid-template-columns:repeat(3, minmax(0, 1fr))}.klods-grid--cols-4{grid-template-columns:repeat(4, minmax(0, 1fr))}.klods-grid--cols-5{grid-template-columns:repeat(5, minmax(0, 1fr))}.klods-grid--cols-6{grid-template-columns:repeat(6, minmax(0, 1fr))}.klods-grid--fit{--klods-grid-min: 16rem;grid-template-columns:repeat(auto-fit, minmax(var(--klods-grid-min), 1fr))}.klods-center{display:flex;align-items:center;justify-content:center}.klods-spread{display:flex;align-items:center;justify-content:space-between;gap:var(--klods-space-4)}.klods-push{margin-inline-start:auto}.klods-fill{display:flex;align-items:center;gap:var(--klods-space-4);flex:1 1 auto;min-width:0;min-height:0}.klods-stack--gap-0,.klods-cluster--gap-0,.klods-row--gap-0,.klods-grid--gap-0{gap:var(--klods-space-0)}.klods-stack--gap-1,.klods-cluster--gap-1,.klods-row--gap-1,.klods-grid--gap-1{gap:var(--klods-space-1)}.klods-stack--gap-2,.klods-cluster--gap-2,.klods-row--gap-2,.klods-grid--gap-2{gap:var(--klods-space-2)}.klods-stack--gap-3,.klods-cluster--gap-3,.klods-row--gap-3,.klods-grid--gap-3{gap:var(--klods-space-3)}.klods-stack--gap-4,.klods-cluster--gap-4,.klods-row--gap-4,.klods-grid--gap-4{gap:var(--klods-space-4)}.klods-stack--gap-5,.klods-cluster--gap-5,.klods-row--gap-5,.klods-grid--gap-5{gap:var(--klods-space-5)}.klods-stack--gap-6,.klods-cluster--gap-6,.klods-row--gap-6,.klods-grid--gap-6{gap:var(--klods-space-6)}.klods-stack--gap-7,.klods-cluster--gap-7,.klods-row--gap-7,.klods-grid--gap-7{gap:var(--klods-space-7)}.klods-stack--gap-8,.klods-cluster--gap-8,.klods-row--gap-8,.klods-grid--gap-8{gap:var(--klods-space-8)}.klods-pad-0{padding:var(--klods-space-0)}.klods-pad-1{padding:var(--klods-space-1)}.klods-pad-2{padding:var(--klods-space-2)}.klods-pad-3{padding:var(--klods-space-3)}.klods-pad-4{padding:var(--klods-space-4)}.klods-pad-5{padding:var(--klods-space-5)}.klods-pad-6{padding:var(--klods-space-6)}.klods-pad-7{padding:var(--klods-space-7)}.klods-pad-8{padding:var(--klods-space-8)}.klods-row--inline{display:inline-flex}.klods-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}}
1
+ @layer klods.reset, klods.tokens, klods.themes, klods.layout, klods.components, klods.utilities;@layer klods.reset{*,*::before,*::after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;text-size-adjust:100%}body{margin:0;min-height:100dvh;font-family:var(--klods-font-sans);font-size:var(--klods-font-size-base);line-height:var(--klods-line-height-base);color:var(--klods-color-fg);background:var(--klods-color-bg)}img,picture,svg,video{display:block;max-width:100%}pre,code{font-family:var(--klods-font-mono)}pre{overflow-x:auto;white-space:pre-wrap;overflow-wrap:break-word}button,input,select,textarea{font:inherit;color:inherit}a{color:var(--klods-color-link)}}@layer klods.tokens{:root{--klods-color-bg: #ffffff;--klods-color-fg: #1a1a1a;--klods-color-muted: #6b7280;--klods-color-surface: #f5f5f7;--klods-color-surface-2: #ececef;--klods-color-border: #e5e7eb;--klods-color-accent: #5b5bd6;--klods-color-accent-fg: #ffffff;--klods-color-link: var(--klods-color-accent);--klods-color-danger: #d1335b;--klods-color-success: #2f9e44;--klods-color-warning: #d97706;--klods-color-info: #2563eb;--klods-font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--klods-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--klods-font-size-base: 16px;--klods-font-size-sm: 0.875rem;--klods-font-size-lg: 1.125rem;--klods-font-size-xl: 1.5rem;--klods-font-size-2xl: 2rem;--klods-font-size-3xl: 2.5rem;--klods-line-height-base: 1.5;--klods-line-height-tight: 1.2;--klods-space-0: 0;--klods-space-1: 0.25rem;--klods-space-2: 0.5rem;--klods-space-3: 0.75rem;--klods-space-4: 1rem;--klods-space-5: 1.5rem;--klods-space-6: 2rem;--klods-space-7: 3rem;--klods-space-8: 4rem;--klods-radius-sm: 4px;--klods-radius-md: 8px;--klods-radius-lg: 16px;--klods-radius-pill: 999px;--klods-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);--klods-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);--klods-shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.12);--klods-content-max: 72rem;--klods-content-narrow: 48rem;--klods-sidebar-width: 16rem;--klods-header-height: 4rem;--klods-footer-height: auto;--klods-gutter: var(--klods-space-5);--klods-transition: 150ms ease}@media(max-width: 48rem){:root{--klods-gutter: var(--klods-space-4)}}}@layer klods.themes{[data-theme=dark]{--klods-color-bg: #0f1115;--klods-color-fg: #ececef;--klods-color-muted: #9aa0aa;--klods-color-surface: #181b22;--klods-color-surface-2: #1f232c;--klods-color-border: #2a2f3a;--klods-color-accent: #8a8aff;--klods-color-accent-fg: #0f1115}[data-theme=playful]{--klods-color-bg: #fff7fb;--klods-color-fg: #2a1a3a;--klods-color-surface: #ffe8f3;--klods-color-surface-2: #ffd6ea;--klods-color-border: #f4bfd9;--klods-color-accent: #e63990;--klods-color-accent-fg: #ffffff;--klods-radius-sm: 8px;--klods-radius-md: 16px;--klods-radius-lg: 28px;--klods-font-sans: "Comic Sans MS", "Comic Sans", ui-rounded, system-ui, sans-serif}[data-theme=brutalist]{--klods-color-bg: #ffffff;--klods-color-fg: #000000;--klods-color-surface: #ffffff;--klods-color-surface-2: #f0f0f0;--klods-color-border: #000000;--klods-color-accent: #ffe600;--klods-color-accent-fg: #000000;--klods-color-link: #000000;--klods-radius-sm: 0;--klods-radius-md: 0;--klods-radius-lg: 0;--klods-radius-pill: 0;--klods-shadow-sm: 4px 4px 0 #000;--klods-shadow-md: 6px 6px 0 #000;--klods-shadow-lg: 10px 10px 0 #000;--klods-font-sans: "Courier New", ui-monospace, monospace}}@layer klods.layout{.klods-page{display:grid;min-height:100dvh;grid-template-columns:1fr;grid-template-rows:auto 1fr auto;grid-template-areas:"header" "content" "footer"}.klods-page--with-sidebar{grid-template-columns:var(--klods-sidebar-width) 1fr;grid-template-areas:"header header" "sidebar content" "footer footer"}.klods-page--with-sidebar.klods-page--sidebar-trailing{grid-template-columns:1fr var(--klods-sidebar-width);grid-template-areas:"header header" "content sidebar" "footer footer"}.klods-header{grid-area:header;display:flex;align-items:center;gap:var(--klods-space-4);padding:0 var(--klods-gutter);min-height:var(--klods-header-height);background:var(--klods-color-surface);border-bottom:1px solid var(--klods-color-border)}.klods-page--sticky-header .klods-header{position:sticky;top:0;z-index:10}html:has(.klods-page--sticky-header){scroll-padding-top:var(--klods-header-height)}.klods-sidebar{grid-area:sidebar;padding:var(--klods-gutter);background:var(--klods-color-surface);border-right:1px solid var(--klods-color-border)}.klods-sidebar>*{position:sticky;top:var(--klods-gutter);max-height:calc(100dvh - var(--klods-gutter)*2);overflow-y:auto}.klods-page--sticky-header .klods-sidebar>*{top:calc(var(--klods-header-height) + var(--klods-gutter));max-height:calc(100dvh - var(--klods-header-height) - var(--klods-gutter)*2)}.klods-page--sidebar-trailing .klods-sidebar{border-right:none;border-left:1px solid var(--klods-color-border)}.klods-content{grid-area:content;padding:var(--klods-gutter);min-width:0;max-width:var(--klods-content-max);margin-inline:auto;width:100%}.klods-content--narrow{max-width:var(--klods-content-narrow);margin-inline:auto;width:100%}.klods-footer{grid-area:footer;padding:var(--klods-gutter);background:var(--klods-color-surface);border-top:1px solid var(--klods-color-border);color:var(--klods-color-muted)}@media(max-width: 48rem){.klods-page--with-sidebar,.klods-page--with-sidebar.klods-page--sidebar-trailing{grid-template-columns:1fr;grid-template-areas:"header" "content" "footer"}.klods-page--with-sidebar>.klods-sidebar{display:none;position:fixed;inset-block:0;inset-inline-start:0;width:min(18rem,85vw);z-index:7;border-right:1px solid var(--klods-color-border);border-bottom:none;overflow-y:auto;box-shadow:var(--klods-shadow-lg)}.klods-page[data-sidebar-open]>.klods-sidebar{display:block}.klods-page[data-sidebar-open]::before{content:"";position:fixed;inset:0;z-index:5;background:rgba(0,0,0,.4)}.klods-page--with-sidebar>.klods-header{isolation:isolate;z-index:6}.klods-page--with-sidebar>.klods-sidebar>*{position:static;max-height:none;overflow-y:visible}}.klods-sidebar-toggle{display:none;align-items:center;justify-content:center;padding:var(--klods-space-2);border-radius:var(--klods-radius-sm);background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);color:inherit;cursor:pointer;line-height:1;transition:background var(--klods-transition)}.klods-sidebar-toggle:hover,.klods-sidebar-toggle:focus-visible{background:var(--klods-color-surface-2)}.klods-sidebar-toggle:focus-visible{outline:2px solid var(--klods-color-accent);outline-offset:2px}@media(max-width: 48rem){.klods-sidebar-toggle{display:inline-flex}}.klods-section{scroll-margin-top:var(--klods-space-5)}.klods-section+.klods-section{margin-block-start:var(--klods-space-7);padding-block-start:var(--klods-space-6);border-block-start:1px solid var(--klods-color-border)}}@layer klods.components{.klods-nav{display:flex;align-items:center;gap:var(--klods-space-4)}.klods-nav__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--klods-space-3);list-style:none;padding:0;margin:0}.klods-nav__link{display:inline-flex;align-items:center;padding:var(--klods-space-2) var(--klods-space-3);border-radius:var(--klods-radius-sm);color:inherit;text-decoration:none;transition:background var(--klods-transition)}.klods-nav__link:hover,.klods-nav__link:focus-visible{background:var(--klods-color-surface-2)}.klods-nav__link--active{background:var(--klods-color-accent);color:var(--klods-color-accent-fg)}.klods-nav__toggle{display:none;align-items:center;justify-content:center;padding:var(--klods-space-2);border-radius:var(--klods-radius-sm);background:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);color:inherit;cursor:pointer;line-height:1;transition:background var(--klods-transition)}.klods-nav__toggle:hover,.klods-nav__toggle:focus-visible{background:var(--klods-color-surface-2)}.klods-nav__toggle:focus-visible{outline:2px solid var(--klods-color-accent);outline-offset:2px}@media(max-width: 48rem){.klods-nav--collapse{flex-wrap:wrap}.klods-nav--collapse .klods-nav__toggle{display:inline-flex}.klods-nav--collapse .klods-nav__list{display:none;width:100%;flex-direction:column;align-items:stretch;gap:var(--klods-space-1);padding-block:var(--klods-space-2)}.klods-nav--collapse[data-nav-open] .klods-nav__list{display:flex}}.klods-toc{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--klods-space-1)}.klods-toc a{display:block;padding:var(--klods-space-2) var(--klods-space-3);border-radius:var(--klods-radius-sm);color:inherit;text-decoration:none;transition:background var(--klods-transition)}.klods-toc a:hover{background:var(--klods-color-surface-2)}.klods-toc--sub{padding-inline-start:var(--klods-space-3);margin-top:var(--klods-space-1)}.klods-toc--sub a{font-size:var(--klods-font-size-sm);color:var(--klods-color-muted);padding:var(--klods-space-1) var(--klods-space-3)}.klods-toc--sub a:hover{color:var(--klods-color-fg)}.klods-card{display:flex;flex-direction:column;gap:var(--klods-space-3);padding:var(--klods-space-5);background:var(--klods-color-surface);border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-md)}.klods-card--elevated{border:none;box-shadow:var(--klods-shadow-md)}.klods-card__title{margin:0;font-size:var(--klods-font-size-lg);line-height:var(--klods-line-height-tight)}.klods-card__body{margin:0}.klods-card__footer{display:flex;gap:var(--klods-space-3);padding-top:var(--klods-space-3);border-top:1px solid var(--klods-color-border)}.klods-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--klods-space-2);padding:var(--klods-space-2) var(--klods-space-4);font:inherit;font-weight:600;color:var(--klods-color-fg);background:var(--klods-color-surface-2);border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-sm);cursor:pointer;transition:background var(--klods-transition),filter var(--klods-transition),transform var(--klods-transition)}.klods-button:hover{filter:brightness(0.95)}.klods-button:active{transform:translateY(1px)}.klods-button--primary{background:var(--klods-color-accent);color:var(--klods-color-accent-fg);border-color:rgba(0,0,0,0)}.klods-button--primary:hover{filter:brightness(0.95);background:var(--klods-color-accent)}.klods-button--danger{background:var(--klods-color-danger);color:#fff;border-color:rgba(0,0,0,0)}.klods-button--danger:hover{filter:brightness(0.9);background:var(--klods-color-danger)}.klods-button--ghost{background:rgba(0,0,0,0);border-color:rgba(0,0,0,0)}.klods-button--ghost:hover{background:var(--klods-color-surface-2);filter:none}.klods-button-group{display:inline-flex;border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-pill)}.klods-button-group>.klods-button{border-radius:0;border-color:rgba(0,0,0,0);position:relative}.klods-button-group>.klods-button:not(:last-child){border-inline-end:1px solid var(--klods-color-border)}.klods-button-group>.klods-button:first-child{border-start-start-radius:var(--klods-radius-pill);border-end-start-radius:var(--klods-radius-pill)}.klods-button-group>.klods-button:last-child{border-start-end-radius:var(--klods-radius-pill);border-end-end-radius:var(--klods-radius-pill)}.klods-button-group>.klods-button:hover,.klods-button-group>.klods-button:focus-visible{z-index:1}.klods-button-group>.klods-button[aria-pressed=true]{background:var(--klods-color-accent);color:var(--klods-color-accent-fg);border-color:var(--klods-color-accent);z-index:1}.klods-badge{display:inline-flex;align-items:center;padding:.125rem var(--klods-space-2);font-size:var(--klods-font-size-sm);font-weight:600;color:var(--klods-color-fg);background:var(--klods-color-surface-2);border-radius:var(--klods-radius-pill)}.klods-badge--accent{background:var(--klods-color-accent);color:var(--klods-color-accent-fg)}.klods-badge--success{background:var(--klods-color-success);color:#fff}.klods-badge--danger{background:var(--klods-color-danger);color:#fff}.klods-alert{padding:var(--klods-space-4);border-radius:var(--klods-radius-md);border:1px solid var(--klods-color-border);background:var(--klods-color-surface)}.klods-alert--info{border-color:var(--klods-color-info);background:color-mix(in srgb, var(--klods-color-info) 12%, var(--klods-color-surface))}.klods-alert--success{border-color:var(--klods-color-success);background:color-mix(in srgb, var(--klods-color-success) 12%, var(--klods-color-surface))}.klods-alert--warning{border-color:var(--klods-color-warning);background:color-mix(in srgb, var(--klods-color-warning) 12%, var(--klods-color-surface))}.klods-alert--danger{border-color:var(--klods-color-danger);background:color-mix(in srgb, var(--klods-color-danger) 12%, var(--klods-color-surface))}.klods-prose>*+*{margin-block-start:var(--klods-space-4)}.klods-prose h1,.klods-prose h2,.klods-prose h3{line-height:var(--klods-line-height-tight)}.klods-prose code{font-family:var(--klods-font-mono);font-size:.95em;padding:.1em .35em;background:var(--klods-color-surface-2);border-radius:var(--klods-radius-sm)}.klods-prose pre{font-family:var(--klods-font-mono);padding:var(--klods-space-4);overflow-x:auto;background:var(--klods-color-surface-2);border-radius:var(--klods-radius-md)}.klods-prose pre code{padding:0;background:rgba(0,0,0,0)}code{font-size:.9em;padding:.1em .35em;background:var(--klods-color-surface-2);border-radius:var(--klods-radius-sm)}pre{padding:var(--klods-space-4);background:var(--klods-color-surface-2);border-radius:var(--klods-radius-md)}pre code{padding:0;background:rgba(0,0,0,0);font-size:inherit}.klods-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}.klods-table{width:100%;border-collapse:collapse}.klods-table th,.klods-table td{padding:var(--klods-space-3) var(--klods-space-4);text-align:left;border-bottom:1px solid var(--klods-color-border)}.klods-table th{font-weight:600;background:var(--klods-color-surface)}.klods-table--striped tbody tr:nth-child(even) td{background:var(--klods-color-surface)}.klods-table--dense th,.klods-table--dense td{padding:var(--klods-space-2) var(--klods-space-3)}.klods-box{padding:var(--klods-space-3);background:var(--klods-color-surface-2);border-radius:var(--klods-radius-sm);text-align:center}.klods-muted{color:var(--klods-color-muted)}.klods-text-center{text-align:center}.klods-lead{font-size:var(--klods-font-size-lg);color:var(--klods-color-muted)}.klods-form{display:flex;flex-direction:column;gap:var(--klods-space-5)}.klods-field{display:flex;flex-direction:column;gap:var(--klods-space-2)}.klods-label{font-size:var(--klods-font-size-sm);font-weight:600;color:var(--klods-color-fg)}.klods-label--required::after{content:" *";color:var(--klods-color-danger);font-weight:400}.klods-input,.klods-select,.klods-textarea{display:block;width:100%;padding:var(--klods-space-2) var(--klods-space-3);font:inherit;color:var(--klods-color-fg);background:var(--klods-color-bg);border:1px solid var(--klods-color-border);border-radius:var(--klods-radius-sm);transition:border-color var(--klods-transition),box-shadow var(--klods-transition);appearance:none}.klods-input::placeholder,.klods-textarea::placeholder{color:var(--klods-color-muted)}.klods-input:hover,.klods-select:hover,.klods-textarea:hover{border-color:var(--klods-color-muted)}.klods-input:focus,.klods-select:focus,.klods-textarea:focus{outline:none;border-color:var(--klods-color-accent);box-shadow:0 0 0 3px color-mix(in srgb, var(--klods-color-accent) 20%, transparent)}.klods-input:disabled,.klods-select:disabled,.klods-textarea:disabled{opacity:.5;cursor:not-allowed;background:var(--klods-color-surface-2)}.klods-input--sm{padding:var(--klods-space-1) var(--klods-space-2);font-size:var(--klods-font-size-sm)}.klods-input--lg{padding:var(--klods-space-3) var(--klods-space-4);font-size:var(--klods-font-size-lg)}.klods-input--range{display:flex;align-items:center;gap:var(--klods-space-3);cursor:default}.klods-input--range input{flex:1 1 auto;padding:0;border:none;background:rgba(0,0,0,0);appearance:auto;accent-color:var(--klods-color-accent)}.klods-input--range output{min-width:3ch;text-align:right;font-variant-numeric:tabular-nums}.klods-input--color{display:flex;align-items:center;gap:var(--klods-space-3)}.klods-input--color input{width:2rem;height:1.5rem;padding:0;border:0;background:none;appearance:auto;cursor:pointer}.klods-select-wrapper{position:relative;display:block}.klods-select-wrapper::after{content:"";position:absolute;inset-inline-end:var(--klods-space-3);top:50%;transform:translateY(-50%);width:1rem;height:1rem;background-color:var(--klods-color-muted);mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m4 6 4 4 4-4'/%3E%3C/svg%3E");mask-repeat:no-repeat;mask-size:contain;pointer-events:none}.klods-select-wrapper:has(.klods-select:disabled)::after{opacity:.5}.klods-select{padding-inline-end:calc(var(--klods-space-3)*2 + 1rem)}.klods-textarea{resize:vertical;min-height:7rem}.klods-help{font-size:var(--klods-font-size-sm);color:var(--klods-color-muted);margin-top:calc(var(--klods-space-1) - var(--klods-space-2))}.klods-error{font-size:var(--klods-font-size-sm);color:var(--klods-color-danger);display:none;margin-top:calc(var(--klods-space-1) - var(--klods-space-2))}.klods-field--invalid .klods-input,.klods-field--invalid .klods-select,.klods-field--invalid .klods-textarea{border-color:var(--klods-color-danger)}.klods-field--invalid .klods-input:focus,.klods-field--invalid .klods-select:focus,.klods-field--invalid .klods-textarea:focus{box-shadow:0 0 0 3px color-mix(in srgb, var(--klods-color-danger) 20%, transparent)}.klods-field--invalid .klods-error{display:block}.klods-field--invalid .klods-help{display:none}.klods-checkbox,.klods-radio{display:inline-flex;align-items:center;gap:var(--klods-space-2);cursor:pointer;user-select:none}.klods-checkbox input[type=checkbox],.klods-checkbox input[type=radio],.klods-radio input[type=checkbox],.klods-radio input[type=radio]{accent-color:var(--klods-color-accent);width:1rem;height:1rem;margin:0;flex-shrink:0;cursor:pointer}.klods-checkbox:has(input:disabled),.klods-radio:has(input:disabled){opacity:.5;cursor:not-allowed}.klods-checkbox:has(input:disabled) input,.klods-radio:has(input:disabled) input{cursor:not-allowed}.klods-switch{position:relative;display:inline-flex;align-items:center;gap:var(--klods-space-3);cursor:pointer;user-select:none}.klods-switch:has(.klods-switch__input:disabled){opacity:.5;cursor:not-allowed}.klods-switch__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}.klods-switch__track{position:relative;display:inline-block;flex-shrink:0;width:2.5rem;height:1.5rem;background:var(--klods-color-border);border-radius:var(--klods-radius-pill);transition:background var(--klods-transition)}.klods-switch__track::after{content:"";position:absolute;top:.1875rem;left:.1875rem;width:1.125rem;height:1.125rem;background:var(--klods-color-bg);border-radius:50%;box-shadow:var(--klods-shadow-sm);transition:transform var(--klods-transition)}.klods-switch__input:checked+.klods-switch__track{background:var(--klods-color-accent)}.klods-switch__input:checked+.klods-switch__track::after{transform:translateX(1rem)}.klods-switch__input:focus-visible+.klods-switch__track{outline:2px solid var(--klods-color-accent);outline-offset:2px}.klods-switch__label{line-height:var(--klods-line-height-tight)}.klods-switch--reverse{display:flex;width:100%;flex-direction:row-reverse;justify-content:space-between}@media(max-width: 48rem){.klods-input,.klods-select{min-height:2.75rem}.klods-checkbox,.klods-radio{min-height:2.75rem}}}@layer klods.utilities{.klods-stack{display:flex;flex-direction:column;gap:var(--klods-space-4)}.klods-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--klods-space-4)}.klods-row{display:flex;flex-direction:row;align-items:center;gap:var(--klods-space-4)}.klods-grid{display:grid;gap:var(--klods-space-4);grid-template-columns:repeat(2, minmax(0, 1fr))}.klods-grid--cols-1{grid-template-columns:repeat(1, minmax(0, 1fr))}.klods-grid--cols-2{grid-template-columns:repeat(2, minmax(0, 1fr))}.klods-grid--cols-3{grid-template-columns:repeat(3, minmax(0, 1fr))}.klods-grid--cols-4{grid-template-columns:repeat(4, minmax(0, 1fr))}.klods-grid--cols-5{grid-template-columns:repeat(5, minmax(0, 1fr))}.klods-grid--cols-6{grid-template-columns:repeat(6, minmax(0, 1fr))}.klods-grid--fit{--klods-grid-min: 16rem;grid-template-columns:repeat(auto-fit, minmax(var(--klods-grid-min), 1fr))}.klods-center{display:flex;align-items:center;justify-content:center}.klods-spread{display:flex;align-items:center;justify-content:space-between;gap:var(--klods-space-4)}.klods-push{margin-inline-start:auto}.klods-fill{display:flex;align-items:center;gap:var(--klods-space-4);flex:1 1 auto;min-width:0;min-height:0}.klods-stack--gap-0,.klods-cluster--gap-0,.klods-row--gap-0,.klods-grid--gap-0{gap:var(--klods-space-0)}.klods-stack--gap-1,.klods-cluster--gap-1,.klods-row--gap-1,.klods-grid--gap-1{gap:var(--klods-space-1)}.klods-stack--gap-2,.klods-cluster--gap-2,.klods-row--gap-2,.klods-grid--gap-2{gap:var(--klods-space-2)}.klods-stack--gap-3,.klods-cluster--gap-3,.klods-row--gap-3,.klods-grid--gap-3{gap:var(--klods-space-3)}.klods-stack--gap-4,.klods-cluster--gap-4,.klods-row--gap-4,.klods-grid--gap-4{gap:var(--klods-space-4)}.klods-stack--gap-5,.klods-cluster--gap-5,.klods-row--gap-5,.klods-grid--gap-5{gap:var(--klods-space-5)}.klods-stack--gap-6,.klods-cluster--gap-6,.klods-row--gap-6,.klods-grid--gap-6{gap:var(--klods-space-6)}.klods-stack--gap-7,.klods-cluster--gap-7,.klods-row--gap-7,.klods-grid--gap-7{gap:var(--klods-space-7)}.klods-stack--gap-8,.klods-cluster--gap-8,.klods-row--gap-8,.klods-grid--gap-8{gap:var(--klods-space-8)}.klods-pad-0{padding:var(--klods-space-0)}.klods-pad-1{padding:var(--klods-space-1)}.klods-pad-2{padding:var(--klods-space-2)}.klods-pad-3{padding:var(--klods-space-3)}.klods-pad-4{padding:var(--klods-space-4)}.klods-pad-5{padding:var(--klods-space-5)}.klods-pad-6{padding:var(--klods-space-6)}.klods-pad-7{padding:var(--klods-space-7)}.klods-pad-8{padding:var(--klods-space-8)}.klods-row--inline{display:inline-flex}.klods-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0}@media(max-width: 48rem){.klods-grid--cols-3,.klods-grid--cols-4,.klods-grid--cols-5,.klods-grid--cols-6{grid-template-columns:repeat(2, minmax(0, 1fr))}}@media(max-width: 30rem){[class*=klods-grid--cols-]{grid-template-columns:1fr}}@media(max-width: 30rem){.klods-hide-mobile{display:none !important}}@media(max-width: 48rem){.klods-hide-tablet{display:none !important}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klods-css",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "klods stylesheet — drop-in CSS for layout, utilities and components.",
5
5
  "license": "MIT",
6
6
  "author": "Drue Wilding",
@@ -20,12 +20,14 @@
20
20
  "exports": {
21
21
  ".": "./dist/klods.css",
22
22
  "./min": "./dist/klods.min.css",
23
+ "./js": "./dist/klods.js",
23
24
  "./src/*": "./src/*"
24
25
  },
25
26
  "scripts": {
26
- "build": "npm run build:css && npm run build:min",
27
+ "build": "npm run build:css && npm run build:min && npm run build:js",
27
28
  "build:css": "sass src/klods.scss dist/klods.css --style=expanded --no-source-map",
28
29
  "build:min": "sass src/klods.scss dist/klods.min.css --style=compressed --no-source-map",
30
+ "build:js": "cp src/klods.js dist/klods.js",
29
31
  "dev": "sass src/klods.scss dist/klods.css --watch --style=expanded"
30
32
  },
31
33
  "devDependencies": {
@@ -1,5 +1,6 @@
1
1
  // Components — first wave: nav, card, button, badge, alert, prose.
2
2
  // More to follow (forms, table, modal, tabs, breadcrumbs, toast).
3
+ @use "tokens" as t;
3
4
 
4
5
  @layer klods.components {
5
6
  // ── Nav ────────────────────────────────────────────────────────────────
@@ -39,6 +40,55 @@
39
40
  color: var(--klods-color-accent-fg);
40
41
  }
41
42
 
43
+ // ── Nav hamburger toggle ───────────────────────────────────────────────
44
+ // Visible only inside .klods-nav--collapse on narrow viewports.
45
+ .klods-nav__toggle {
46
+ display: none;
47
+ align-items: center;
48
+ justify-content: center;
49
+ padding: var(--klods-space-2);
50
+ border-radius: var(--klods-radius-sm);
51
+ background: transparent;
52
+ border: 1px solid transparent;
53
+ color: inherit;
54
+ cursor: pointer;
55
+ line-height: 1;
56
+ transition: background var(--klods-transition);
57
+
58
+ &:hover,
59
+ &:focus-visible {
60
+ background: var(--klods-color-surface-2);
61
+ }
62
+
63
+ &:focus-visible {
64
+ outline: 2px solid var(--klods-color-accent);
65
+ outline-offset: 2px;
66
+ }
67
+ }
68
+
69
+ @media (max-width: #{t.$klods-bp-md}) {
70
+ .klods-nav--collapse {
71
+ flex-wrap: wrap;
72
+
73
+ .klods-nav__toggle {
74
+ display: inline-flex;
75
+ }
76
+
77
+ .klods-nav__list {
78
+ display: none;
79
+ width: 100%;
80
+ flex-direction: column;
81
+ align-items: stretch;
82
+ gap: var(--klods-space-1);
83
+ padding-block: var(--klods-space-2);
84
+ }
85
+
86
+ &[data-nav-open] .klods-nav__list {
87
+ display: flex;
88
+ }
89
+ }
90
+ }
91
+
42
92
  // ── Toc ────────────────────────────────────────────────────────────────
43
93
  .klods-toc {
44
94
  list-style: none;
@@ -317,6 +367,12 @@
317
367
  }
318
368
 
319
369
  // ── Table ─────────────────────────────────────────────────────────────
370
+ // Wrap a table in .klods-table-wrap to get horizontal scroll on small screens.
371
+ .klods-table-wrap {
372
+ overflow-x: auto;
373
+ -webkit-overflow-scrolling: touch;
374
+ }
375
+
320
376
  .klods-table {
321
377
  width: 100%;
322
378
  border-collapse: collapse;
@@ -672,4 +728,18 @@
672
728
  flex-direction: row-reverse;
673
729
  justify-content: space-between;
674
730
  }
731
+
732
+ // ── Mobile form tap targets ────────────────────────────────────────────
733
+ // WCAG 2.5.8 requires a minimum 24×24px target; aim for ~44px on touch screens.
734
+ @media (max-width: #{t.$klods-bp-md}) {
735
+ .klods-input,
736
+ .klods-select {
737
+ min-height: 2.75rem; // ~44px
738
+ }
739
+
740
+ .klods-checkbox,
741
+ .klods-radio {
742
+ min-height: 2.75rem;
743
+ }
744
+ }
675
745
  }
package/src/_layout.scss CHANGED
@@ -3,6 +3,7 @@
3
3
  // .klods-page is a CSS Grid container that can be:
4
4
  // - default : header / main / footer
5
5
  // - --with-sidebar : adds a left sidebar column
6
+ @use "tokens" as t;
6
7
  // - --with-sidebar.--sidebar-trailing : sidebar on the trailing side
7
8
 
8
9
  @layer klods.layout {
@@ -104,22 +105,91 @@
104
105
  color: var(--klods-color-muted);
105
106
  }
106
107
 
107
- // Collapse sidebar on small screens — stack everything.
108
- @media (max-width: 48rem) {
108
+ // Collapse sidebar on small screens — render as a fixed drawer overlay.
109
+ // Use direct-child `>` selectors throughout so nested .klods-page examples
110
+ // inside the docs (or any app) are not affected.
111
+ @media (max-width: #{t.$klods-bp-md}) {
109
112
  .klods-page--with-sidebar,
110
113
  .klods-page--with-sidebar.klods-page--sidebar-trailing {
111
114
  grid-template-columns: 1fr;
112
115
  grid-template-areas:
113
116
  "header"
114
- "sidebar"
115
117
  "content"
116
118
  "footer";
117
119
  }
118
120
 
119
- .klods-sidebar {
120
- border-right: none;
121
- border-left: none;
122
- border-bottom: 1px solid var(--klods-color-border);
121
+ .klods-page--with-sidebar > .klods-sidebar {
122
+ // Remove from flow entirely — becomes a fixed drawer.
123
+ display: none;
124
+ position: fixed;
125
+ inset-block: 0;
126
+ inset-inline-start: 0;
127
+ width: min(18rem, 85vw);
128
+ z-index: 7; // above backdrop (5) and header (6)
129
+ border-right: 1px solid var(--klods-color-border);
130
+ border-bottom: none;
131
+ overflow-y: auto;
132
+ box-shadow: var(--klods-shadow-lg);
133
+ }
134
+
135
+ .klods-page[data-sidebar-open] > .klods-sidebar {
136
+ display: block;
137
+ }
138
+
139
+ // Backdrop — dims content behind the open drawer.
140
+ // z-index 5 keeps it below the header (6) so the toggle button stays clickable.
141
+ .klods-page[data-sidebar-open]::before {
142
+ content: "";
143
+ position: fixed;
144
+ inset: 0;
145
+ z-index: 5;
146
+ background: rgba(0, 0, 0, 0.4);
147
+ }
148
+
149
+ // Give the header a stacking context so it sits above the backdrop (z-index 5).
150
+ // isolation: isolate creates a new stacking context without affecting position,
151
+ // so sticky headers remain sticky.
152
+ .klods-page--with-sidebar > .klods-header {
153
+ isolation: isolate;
154
+ z-index: 6;
155
+ }
156
+
157
+ // Sidebar sticky children don't make sense inside a scrollable drawer.
158
+ .klods-page--with-sidebar > .klods-sidebar > * {
159
+ position: static;
160
+ max-height: none;
161
+ overflow-y: visible;
162
+ }
163
+ }
164
+
165
+ // Sidebar toggle button — visible only on mobile.
166
+ .klods-sidebar-toggle {
167
+ display: none;
168
+ align-items: center;
169
+ justify-content: center;
170
+ padding: var(--klods-space-2);
171
+ border-radius: var(--klods-radius-sm);
172
+ background: transparent;
173
+ border: 1px solid transparent;
174
+ color: inherit;
175
+ cursor: pointer;
176
+ line-height: 1;
177
+ transition: background var(--klods-transition);
178
+
179
+ &:hover,
180
+ &:focus-visible {
181
+ background: var(--klods-color-surface-2);
182
+ }
183
+
184
+ &:focus-visible {
185
+ outline: 2px solid var(--klods-color-accent);
186
+ outline-offset: 2px;
187
+ }
188
+ }
189
+
190
+ @media (max-width: #{t.$klods-bp-md}) {
191
+ .klods-sidebar-toggle {
192
+ display: inline-flex;
123
193
  }
124
194
  }
125
195
 
package/src/_tokens.scss CHANGED
@@ -1,3 +1,10 @@
1
+ // ── Breakpoints (SCSS-only — not CSS custom properties) ───────────────────
2
+ // Used in @media queries throughout the codebase. Do not change these values
3
+ // without auditing all media queries.
4
+ $klods-bp-sm: 30rem; // 480px — phone landscape
5
+ $klods-bp-md: 48rem; // 768px — tablet
6
+ $klods-bp-lg: 64rem; // 1024px — desktop
7
+
1
8
  @layer klods.tokens {
2
9
  :root {
3
10
  // ── Colour ─────────────────────────────────────────────────────────────
@@ -60,4 +67,11 @@
60
67
  // ── Motion ─────────────────────────────────────────────────────────────
61
68
  --klods-transition: 150ms ease;
62
69
  }
70
+
71
+ // Reduce gutter on small screens so content isn't over-padded on phones.
72
+ @media (max-width: #{$klods-bp-md}) {
73
+ :root {
74
+ --klods-gutter: var(--klods-space-4);
75
+ }
76
+ }
63
77
  }
@@ -2,6 +2,7 @@
2
2
  //
3
3
  // Naming follows BEM-ish conventions: base class with --modifier variants.
4
4
  // All gaps default to --klods-space-4 and can be overridden with --gap-{1..8}.
5
+ @use "tokens" as t;
5
6
 
6
7
  @layer klods.utilities {
7
8
  // Vertical stack
@@ -110,4 +111,36 @@
110
111
  white-space: nowrap;
111
112
  border: 0;
112
113
  }
114
+
115
+ // Responsive grid column collapse — wide grids reduce on smaller screens
116
+ // so content never overflows on phones.
117
+ @media (max-width: #{t.$klods-bp-md}) {
118
+ .klods-grid--cols-3,
119
+ .klods-grid--cols-4,
120
+ .klods-grid--cols-5,
121
+ .klods-grid--cols-6 {
122
+ grid-template-columns: repeat(2, minmax(0, 1fr));
123
+ }
124
+ }
125
+
126
+ @media (max-width: #{t.$klods-bp-sm}) {
127
+ [class*="klods-grid--cols-"] {
128
+ grid-template-columns: 1fr;
129
+ }
130
+ }
131
+
132
+ // Responsive visibility helpers
133
+ // klods-hide-mobile — hidden on phones (≤ 30rem / 480px)
134
+ // klods-hide-tablet — hidden on phones + tablets (≤ 48rem / 768px)
135
+ @media (max-width: #{t.$klods-bp-sm}) {
136
+ .klods-hide-mobile {
137
+ display: none !important;
138
+ }
139
+ }
140
+
141
+ @media (max-width: #{t.$klods-bp-md}) {
142
+ .klods-hide-tablet {
143
+ display: none !important;
144
+ }
145
+ }
113
146
  }
package/src/klods.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * klods.js — interactive helpers for vanilla HTML pages.
3
+ *
4
+ * A tiny companion to klods-css that wires up sidebar and nav toggling
5
+ * without requiring a build step or klods-js.
6
+ *
7
+ * Usage:
8
+ * <script src="https://unpkg.com/klods-css/dist/klods.js"></script>
9
+ *
10
+ * The helpers are exposed on `window.klods`:
11
+ * klods.toggleSidebar(buttonEl)
12
+ * klods.toggleNav(buttonEl)
13
+ */
14
+ (function (global) {
15
+ "use strict";
16
+
17
+ /**
18
+ * Toggle the open/closed state of the sidebar drawer.
19
+ * Pass the toggle button element (or any element inside .klods-page).
20
+ *
21
+ * On first call per page element, auto-wires:
22
+ * - Clicking a link inside the sidebar closes the drawer.
23
+ * - Clicking the backdrop (outside sidebar and header) closes the drawer.
24
+ */
25
+ function toggleSidebar(targetEl) {
26
+ var pageEl = targetEl.closest(".klods-page");
27
+ if (!pageEl) return;
28
+
29
+ if (!pageEl.hasAttribute("data-sidebar-wired")) {
30
+ pageEl.setAttribute("data-sidebar-wired", "");
31
+ var sidebarEl = pageEl.querySelector(":scope > .klods-sidebar");
32
+ if (sidebarEl) {
33
+ sidebarEl.addEventListener("click", function (e) {
34
+ e.stopPropagation();
35
+ });
36
+ sidebarEl.addEventListener("click", function (e) {
37
+ if (e.target.closest("a")) pageEl.removeAttribute("data-sidebar-open");
38
+ });
39
+ }
40
+ pageEl.addEventListener("click", function (e) {
41
+ if (!pageEl.hasAttribute("data-sidebar-open")) return;
42
+ var headerEl = pageEl.querySelector(":scope > .klods-header");
43
+ if (headerEl && headerEl.contains(e.target)) return;
44
+ pageEl.removeAttribute("data-sidebar-open");
45
+ });
46
+ }
47
+
48
+ if (pageEl.hasAttribute("data-sidebar-open")) {
49
+ pageEl.removeAttribute("data-sidebar-open");
50
+ } else {
51
+ pageEl.setAttribute("data-sidebar-open", "");
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Toggle the open/closed state of a .klods-nav--collapse element.
57
+ * Pass the toggle button element (or any element inside the nav).
58
+ */
59
+ function toggleNav(targetEl) {
60
+ var navEl = targetEl.closest(".klods-nav--collapse");
61
+ if (!navEl) return;
62
+ if (navEl.hasAttribute("data-nav-open")) {
63
+ navEl.removeAttribute("data-nav-open");
64
+ } else {
65
+ navEl.setAttribute("data-nav-open", "");
66
+ }
67
+ }
68
+
69
+ global.klods = { toggleSidebar: toggleSidebar, toggleNav: toggleNav };
70
+ })(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : this);