@kolkrabbi/kol-framework 0.6.4 → 0.8.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/kol-framework.css CHANGED
@@ -76,6 +76,11 @@ body {
76
76
  }
77
77
 
78
78
  @media (min-width: 1280px) { :root { --kol-container-max: 1600px; } }
79
+ /* The ladder's last rung is the law's frame itself: --kol-container-max is the
80
+ * responsive RESOLUTION of --kol-content-shell (kol-theme), not a second width
81
+ * family. Before 2026-07-30 the ladder stopped at 1600 while the stated law
82
+ * said 1800 — same element, two answers (lobby/WidthSystemContradictions). */
83
+ @media (min-width: 1920px) { :root { --kol-container-max: var(--kol-content-shell, 1800px); } }
79
84
 
80
85
  /* :root[data-sidenav="collapsed"] removed 2026-07-29 — the manual collapse
81
86
  * feature died with the editor's exit (brand ruling); the responsive rail
@@ -159,6 +164,35 @@ body {
159
164
  flex-direction: column;
160
165
  }
161
166
 
167
+ /* ───── Full-bleed (THE one mechanism, 2026-07-30) ─────
168
+ * Cancels the page inset with a negative margin — container-relative, so it is
169
+ * correct inside any layout, including .kol-brand-layout's sidenav grid.
170
+ * Promoted from kol-website's local `@utility full-bleed`.
171
+ *
172
+ * THE INSET IS A PARAMETER (2026-07-30, second pass). v1 hardwired
173
+ * --kol-pad-section-x, which silently assumed every consumer pads its pages
174
+ * with the DS ladder. kol-website's apps/web does not — it pads with
175
+ * `.breakpoint-padding` (1 / 1.25 / 1.5rem) and uses zero .kol-page classes,
176
+ * so adopting the hardwired rule over-pulled by 4–24px and every hero
177
+ * overhung the viewport. A cancellation utility must cancel the inset that is
178
+ * ACTUALLY THERE; the DS cannot know that from inside the package.
179
+ *
180
+ * So: --kol-full-bleed-inset defaults to the DS ladder (zero change for
181
+ * consumers on .kol-page) and is overridable by any consumer running its own
182
+ * inset — scope it on the padded wrapper, or on :root for a whole app:
183
+ *
184
+ * :root { --kol-full-bleed-inset: 1rem; }
185
+ * @media (min-width: 768px) { :root { --kol-full-bleed-inset: 1.25rem; } }
186
+ * @media (min-width: 1024px) { :root { --kol-full-bleed-inset: 1.5rem; } }
187
+ *
188
+ * NEVER use the viewport trick (`width:100vw; margin-left: calc(50% - 50vw)`):
189
+ * 50vw measures the VIEWPORT, and inside a sidenav grid the content track is
190
+ * not viewport-centred — the element overhangs left by ~half the sidenav width
191
+ * and any `overflow:hidden` ancestor clips it (the /review slice bug). */
192
+ .kol-full-bleed {
193
+ margin-inline: calc(-1 * var(--kol-full-bleed-inset, var(--kol-pad-section-x)));
194
+ }
195
+
162
196
  /* ───── Standard 4-col content grid ─────
163
197
  The page-wide grid all chapter content lays out against. Always 4 cols.
164
198
  Rows size to content; children control their own height via aspect-ratio
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolkrabbi/kol-framework",
3
- "version": "0.6.4",
3
+ "version": "0.8.0",
4
4
  "description": "KOL app shell — sidenav, layout, theme toggle, footer, heroes + brand color layer. SideNav takes nav data as props so the tree stays app-local.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,8 +27,8 @@
27
27
  "app-shell"
28
28
  ],
29
29
  "dependencies": {
30
- "@kolkrabbi/kol-component": "0.13.0",
31
- "@kolkrabbi/kol-icons": "0.8.10"
30
+ "@kolkrabbi/kol-component": "workspace:*",
31
+ "@kolkrabbi/kol-icons": "workspace:*"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": "^18.3.0 || ^19.0.0",
@@ -43,4 +43,4 @@
43
43
  "publishConfig": {
44
44
  "access": "public"
45
45
  }
46
- }
46
+ }
@@ -36,20 +36,26 @@ const MODE_LABEL = { light: 'Light mode', dark: 'Dark mode', system: 'System' }
36
36
  const NEXT_MODE = { light: 'dark', dark: 'system', system: 'light' }
37
37
 
38
38
  export default function ThemeToggle({ variant = 'icon', size = 'md', className = '' }) {
39
- const { isDark, mode, cycle } = useTheme()
39
+ const { mode, cycle } = useTheme()
40
40
 
41
41
  const next = NEXT_MODE[mode]
42
42
  const nextLabel = next === 'system' ? 'system theme' : `${next} mode`
43
43
  const handleToggle = cycle
44
44
 
45
- /* The roll: the strip slides one glyph-width per mode while the split-circle
46
- * glyphs spin 180° in the same clock — dark→light rolls forward (>>),
47
- * light→dark rolls back (<<), system slides on to the desktop glyph. */
48
- const glyphSpin = {
49
- transform: isDark ? 'rotate(0deg)' : 'rotate(180deg)',
45
+ /* The ROLL (redone 2026-07-30 — the 0.5.13 motion was declared FAILED).
46
+ * Wheel mechanics, not slide+flip: rotation ACCUMULATES with the slot
47
+ * (-180° per glyph-width) and runs on the same 500ms/ease clock as the
48
+ * strip's translateX, so the split-circle reads as a coin rolling to its
49
+ * next position — travel left = CCW, travel right = CW, two slots = two
50
+ * full half-turns. The old code rotated on `isDark` alone, desynced from
51
+ * the travel, which is exactly why it read as a slide with a flip bolted
52
+ * on. The desktop glyph doesn't spin — it's not a wheel; it slides in as
53
+ * the no-choice state. */
54
+ const slot = mode === 'dark' ? 0 : mode === 'light' ? 1 : 2
55
+ const glyphRoll = {
56
+ transform: `rotate(${slot * -180}deg)`,
50
57
  lineHeight: 0,
51
58
  }
52
- const slot = mode === 'dark' ? 0 : mode === 'light' ? 1 : 2
53
59
  const iconSwap = (size) => (
54
60
  <span
55
61
  className="relative inline-block overflow-hidden"
@@ -60,10 +66,10 @@ export default function ThemeToggle({ variant = 'icon', size = 'md', className =
60
66
  className="flex transition-transform duration-500 ease-in-out"
61
67
  style={{ width: size * 3, transform: `translateX(-${slot * size}px)` }}
62
68
  >
63
- <span className="inline-flex transition-transform duration-500 ease-in-out" style={glyphSpin}>
69
+ <span className="inline-flex transition-transform duration-500 ease-in-out" style={glyphRoll}>
64
70
  <Icon name="mode-toggle-01" size={size} />
65
71
  </span>
66
- <span className="inline-flex transition-transform duration-500 ease-in-out" style={glyphSpin}>
72
+ <span className="inline-flex transition-transform duration-500 ease-in-out" style={glyphRoll}>
67
73
  <Icon name="mode-toggle-01" size={size} />
68
74
  </span>
69
75
  <span className="inline-flex" style={{ lineHeight: 0 }}>