@kolkrabbi/kol-framework 0.7.0 → 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 +18 -3
- package/package.json +4 -4
- package/src/ThemeToggle.jsx +15 -9
package/kol-framework.css
CHANGED
|
@@ -167,15 +167,30 @@ body {
|
|
|
167
167
|
/* ───── Full-bleed (THE one mechanism, 2026-07-30) ─────
|
|
168
168
|
* Cancels the page inset with a negative margin — container-relative, so it is
|
|
169
169
|
* correct inside any layout, including .kol-brand-layout's sidenav grid.
|
|
170
|
-
* Promoted from kol-website's local `@utility full-bleed
|
|
171
|
-
*
|
|
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; } }
|
|
172
187
|
*
|
|
173
188
|
* NEVER use the viewport trick (`width:100vw; margin-left: calc(50% - 50vw)`):
|
|
174
189
|
* 50vw measures the VIEWPORT, and inside a sidenav grid the content track is
|
|
175
190
|
* not viewport-centred — the element overhangs left by ~half the sidenav width
|
|
176
191
|
* and any `overflow:hidden` ancestor clips it (the /review slice bug). */
|
|
177
192
|
.kol-full-bleed {
|
|
178
|
-
margin-inline: calc(-1 * var(--kol-pad-section-x));
|
|
193
|
+
margin-inline: calc(-1 * var(--kol-full-bleed-inset, var(--kol-pad-section-x)));
|
|
179
194
|
}
|
|
180
195
|
|
|
181
196
|
/* ───── Standard 4-col content grid ─────
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolkrabbi/kol-framework",
|
|
3
|
-
"version": "0.
|
|
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": "
|
|
31
|
-
"@kolkrabbi/kol-icons": "
|
|
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
|
+
}
|
package/src/ThemeToggle.jsx
CHANGED
|
@@ -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 {
|
|
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
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
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={
|
|
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={
|
|
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 }}>
|