diff --git a/src/components/Stack/styles.module.css b/src/components/Stack/styles.module.css
index b05178bd..27892989 100644
--- a/src/components/Stack/styles.module.css
+++ b/src/components/Stack/styles.module.css
@@ -1,20 +1,26 @@
.stk {
display: flex;
position: relative;
- width: var(--bkkw, auto);
- height: var(--bkkh, auto);
+ width: var(--bkkw);
+ height: var(--bkkh);
}
-.lay .line::after,
+/* Remove borders from children if they're lines */
.stk .line::after {
border: none;
}
+/* Style for debugging border */
.v::after {
content: '';
position: absolute;
inset: 0;
- border: 1px solid var(--bkkcl);
+ border: 1px solid var(--bkkcl, hsla(330, 60%, 40%, 0.6));
pointer-events: none;
- z-index: var(--bkzio);
+ z-index: var(--bkzio, 2);
+}
+
+/* Style for flat debugging background */
+.flat {
+ background-color: var(--bkkcf, hsla(0, 100%, 70%, 0.2));
}
diff --git a/src/components/index.ts b/src/components/index.ts
index 3292f730..fdc311e0 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -4,23 +4,23 @@
* @module baseline-kit/components
*/
-import './styles/index.css'
+// Style imports - these generate CSS files in the dist folder
+import './styles/core.css'
+import './styles/theme.css'
// Layout Components
export * from './Layout'
export * from './Box'
export * from './Stack'
+export * from './Spacer'
+export * from './Padder'
// Grid Components
export * from './Guide'
export * from './Baseline'
-// Spacing Components
-export * from './Spacer'
-export * from './Padder'
-
// Configuration
export * from './Config'
// Types
-export * from './types'
\ No newline at end of file
+export * from './types'
diff --git a/src/components/styles/base.css b/src/components/styles/base.css
new file mode 100644
index 00000000..496f6aaf
--- /dev/null
+++ b/src/components/styles/base.css
@@ -0,0 +1,122 @@
+/**
+ * Baseline Kit Base Styles
+ *
+ * This file defines:
+ * 1. Core system variables used across components
+ * 2. Component-specific CSS variables with fallback values
+ * 3. Robust fallbacks for components to ensure cross-framework compatibility
+ */
+
+/* Core system variables */
+:root {
+ /* Core System Variables */
+ --bkb: 8px; /* Base unit for spacing */
+ --bkzi: 1; /* Base z-index */
+ --bkzio: 2; /* Overlay z-index */
+ --bk-transition-base: 180ms ease; /* Standard transition */
+
+ /* Default Dimensions */
+ --bk-width-default: fit-content;
+ --bk-height-default: fit-content;
+ --bkwf: 100%; /* Full width shorthand */
+ --bkhf: 100%; /* Full height shorthand */
+
+ /* Box Component Variables */
+ --bkxb: var(--bkb);
+ --bkxw: var(--bk-width-default);
+ --bkxh: var(--bk-height-default);
+ --bkxcl: var(--bk-box-color-line-theme, hsla(300, 60%, 40%, 0.6));
+ --bkxcf: var(--bk-box-color-flat-theme, hsla(260, 100%, 70%, 0.2));
+ --bkxci: var(--bk-box-color-text-theme, hsla(300, 60%, 40%, 0.9));
+
+ /* Stack Component Variables */
+ --bkkw: var(--bkwf);
+ --bkkh: var(--bk-height-default);
+ --bkkcl: var(--bk-stack-color-line-theme, hsla(330, 60%, 40%, 0.6));
+ --bkkcf: var(--bk-stack-color-flat-theme, hsla(0, 100%, 70%, 0.2));
+ --bkkci: var(--bk-stack-color-text-theme, hsla(330, 60%, 40%, 0.9));
+
+ /* Baseline Component Variables */
+ --bkbw: 100%;
+ --bkbh: 100%;
+ --bkrt: 0;
+ --bkrh: 1px;
+ --bkbcl: var(--bk-baseline-color-line-theme, hsla(240, 60%, 40%, 0.15));
+ --bkbcf: var(--bk-baseline-color-flat-theme, hsla(230, 100%, 70%, 0.2));
+
+ /* Guide Component Variables */
+ --bkgw: 100vw;
+ --bkgh: 100vh;
+ --bkgg: var(--bkb);
+ --bkgj: center;
+ --bkgt: auto;
+ --bkgpb: 0;
+ --bkgpi: 0;
+ --bkgcl: var(--bk-guide-color-line-theme, hsla(240, 60%, 40%, 0.15));
+ --bkgcp: var(--bk-guide-color-pattern-theme, hsla(230, 100%, 70%, 0.2));
+ --bkgca: var(--bk-guide-color-auto-theme, hsla(200, 100%, 70%, 0.15));
+ --bkgcf: var(--bk-guide-color-fixed-theme, hsla(200, 100%, 70%, 0.15));
+
+ /* Layout Component Variables */
+ --bklw: var(--bk-width-default);
+ --bklh: var(--bk-height-default);
+ --bklcl: var(--bk-layout-color-line-theme, hsla(330, 60%, 40%, 0.6));
+ --bklcf: var(--bk-layout-color-flat-theme, hsla(0, 100%, 70%, 0.2));
+ --bklci: var(--bk-layout-color-text-theme, hsla(330, 60%, 40%, 0.9));
+ --bklgtc: repeat(auto-fill, minmax(100px, 1fr));
+ --bklgtr: auto;
+ --bklg: 0;
+ --bklcg: 0;
+ --bklrg: 0;
+ --bklji: stretch;
+ --bklai: stretch;
+ --bkljc: start;
+ --bklac: start;
+
+ /* Padder Component Variables */
+ --bkpw: var(--bk-width-default);
+ --bkph: var(--bk-height-default);
+ --bkpc: var(--bk-padder-color-theme, hsla(270, 60%, 40%, 0.6));
+
+ /* Spacer Component Variables - with fallback values */
+ --bksw: var(--bkwf);
+ --bksh: var(--bkhf);
+ --bkscl: var(--bk-spacer-color-line-theme, hsla(270, 60%, 40%, 0.6));
+ --bkscf: var(--bk-spacer-color-flat-theme, hsla(230, 100%, 70%, 0.2));
+ --bksct: var(--bk-spacer-color-text-theme, hsla(270, 60%, 40%, 1));
+}
+
+/* Guide Component */
+.guide {
+ --bkgcl: var(--bk-guide-color-line-theme, hsla(240, 60%, 40%, 0.15));
+ --bkgcp: var(--bk-guide-color-pattern-theme, hsla(230, 100%, 70%, 0.2));
+ --bkgca: var(--bk-guide-color-auto-theme, hsla(200, 100%, 70%, 0.15));
+ --bkgcf: var(--bk-guide-color-fixed-theme, hsla(200, 100%, 70%, 0.15));
+}
+
+/* Baseline Component */
+.baseline {
+ --bkbcl: var(--bk-baseline-color-line-theme, hsla(240, 60%, 40%, 0.15));
+ --bkbcf: var(--bk-baseline-color-flat-theme, hsla(230, 100%, 70%, 0.2));
+}
+
+/* Box Component */
+.box {
+ --bkxcl: var(--bk-box-color-line-theme, hsla(300, 60%, 40%, 0.6));
+ --bkxcf: var(--bk-box-color-flat-theme, hsla(260, 100%, 70%, 0.2));
+ --bkxci: var(--bk-box-color-text-theme, hsla(300, 60%, 40%, 0.9));
+}
+
+/* Stack Component */
+.stack {
+ --bkkcl: var(--bk-stack-color-line-theme, hsla(330, 60%, 40%, 0.6));
+ --bkkcf: var(--bk-stack-color-flat-theme, hsla(0, 100%, 70%, 0.2));
+ --bkkci: var(--bk-stack-color-text-theme, hsla(330, 60%, 40%, 0.9));
+}
+
+/* Layout Component */
+.layout {
+ --bklcl: var(--bk-layout-color-line-theme, hsla(330, 60%, 40%, 0.6));
+ --bklcf: var(--bk-layout-color-flat-theme, hsla(0, 100%, 70%, 0.2));
+ --bklci: var(--bk-layout-color-text-theme, hsla(330, 60%, 40%, 0.9));
+}
diff --git a/src/components/styles/core.css b/src/components/styles/core.css
new file mode 100644
index 00000000..7a6c1126
--- /dev/null
+++ b/src/components/styles/core.css
@@ -0,0 +1,40 @@
+/**
+ * Baseline Kit Core Styles
+ *
+ * This file imports all fundamental styles in the correct order:
+ * 1. CSS reset for consistent defaults across browsers
+ * 2. Base variables and common styles
+ * 3. Component-specific styles
+ */
+
+/* CSS reset - Normalizes browser defaults */
+@import './reset.css';
+
+/* Base variables - Core system variables and defaults */
+@import './base.css';
+
+/* Component-specific styles - Each component's styling */
+@import '../Box/styles.module.css';
+@import '../Guide/styles.module.css';
+@import '../Padder/styles.module.css';
+@import '../Spacer/styles.module.css';
+@import '../Stack/styles.module.css';
+@import '../Layout/styles.module.css';
+@import '../Baseline/styles.module.css';
+
+/* Utility classes for visibility control */
+.v {
+ opacity: 1;
+ visibility: visible;
+ transition:
+ opacity var(--bk-transition-base),
+ visibility 0ms linear;
+}
+
+.h {
+ opacity: 0;
+ visibility: hidden;
+ transition:
+ opacity var(--bk-transition-base),
+ visibility 0ms linear 180ms;
+}
diff --git a/src/components/styles/index.css b/src/components/styles/index.css
deleted file mode 100644
index e8177f44..00000000
--- a/src/components/styles/index.css
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Theme must come first to establish CSS variables */
-@import './reset.css';
-
-/* Component-specific styles */
-@import '../Box/styles.module.css';
-@import '../Guide/styles.module.css';
-@import '../Padder/styles.module.css';
-@import '../Spacer/styles.module.css';
-
-/* Base root variables */
-:root {
- /* Core System Variables */
- --bkb: 8px;
- --bkzi: 1;
- --bkzio: 2;
- --bk-transition-base: 180ms ease;
-
- /* Default Dimensions */
- --bk-width-default: fit-content;
- --bk-height-default: fit-content;
- --bkwf: 100%;
- --bkhf: 100%;
-
- /* Box Component */
- --bkxb: var(--bkb);
- --bkxw: var(--bk-width-default);
- --bkxh: var(--bk-height-default);
- --bkxcl: var(--bk-box-color-line-theme);
- --bkxcf: var(--bk-box-color-flat-theme);
- --bkxci: var(--bk-box-color-text-theme);
-
- /* Stack Component */
- --bkkw: var(--bkwf);
- --bkkh: var(--bk-height-default);
- --bkkcl: var(--bk-stack-color-line-theme);
- --bkkcf: var(--bk-stack-color-flat-theme);
- --bkkci: var(--bk-stack-color-text-theme);
-
- /* Baseline Component */
- --bkbw: '100%';
- --bkbh: '100%';
- --bkrt: 0;
- --bkrh: 1px;
- --bkbcl: var(--bk-baseline-color-line-theme);
- --bkbcf: var(--bk-baseline-color-flat-theme);
-
- /* Guide Component */
- --bkgw: 100vw;
- --bkgh: 100vh;
- --bkgg: var(--bkb);
- --bkgj: start;
- --bkgt: auto;
- --bkgpb: 0;
- --bkgpi: 0;
- --bkgcl: var(--bk-guide-color-line-theme);
- --bkgcp: var(--bk-guide-color-pattern-theme);
- --bkgca: var(--bk-guide-color-auto-theme);
- --bkgcf: var(--bk-guide-color-fixed-theme);
-
- /* Layout Component */
- --bklw: var(--bk-width-default);
- --bklh: var(--bk-height-default);
- --bklcl: var(--bk-layout-color-line-theme);
- --bklcf: var(--bk-layout-color-flat-theme);
- --bklci: var(--bk-layout-color-text-theme);
- --bklgtc: repeat(auto-fit, minmax(100px, 1fr));
- --bklgtr: auto;
- --bklg: 0;
- --bklcg: 0;
- --bklrg: 0;
- --bklji: stretch;
- --bklai: stretch;
- --bkljc: start;
- --bklac: start;
-
- /* Padder Component */
- --bkpw: var(--bk-width-default);
- --bkph: var(--bk-height-default);
- --bkpc: var(--bk-padder-color-theme);
-
- /* Spacer Component */
- --bksw: var(--bkwf);
- --bksh: var(--bkhf);
- --bkscl: var(--bk-spacer-color-line-theme);
- --bkscf: var(--bk-spacer-color-flat-theme);
- --bksci: var(--bk-spacer-color-text-theme);
-}
-
-/* Utility classes */
-.v {
- opacity: 1;
- visibility: visible;
- transition: opacity var(--bk-transition-base),
- visibility 0ms linear;
-}
-
-.h {
- opacity: 0;
- visibility: hidden;
- transition: opacity var(--bk-transition-base),
- visibility 0ms linear 180ms;
-}
diff --git a/src/components/styles/index.ts b/src/components/styles/index.ts
new file mode 100644
index 00000000..a458d5ec
--- /dev/null
+++ b/src/components/styles/index.ts
@@ -0,0 +1,15 @@
+/**
+ * Baseline Kit Styles System
+ *
+ * This file exports all core styles needed for the component library.
+ * The import order is important:
+ * 1. core.css - Contains reset.css, base.css, and component styles
+ * 2. theme.css - Contains color variables and theming
+ */
+
+// Core styles must be imported before theme
+import './core.css'
+import './theme.css'
+
+// Empty export to make this a proper module
+export {}
diff --git a/src/components/styles/theme.css b/src/components/styles/theme.css
new file mode 100644
index 00000000..4bbbd68d
--- /dev/null
+++ b/src/components/styles/theme.css
@@ -0,0 +1,142 @@
+/**
+ * Baseline Kit Theme System
+ *
+ * This file contains all color variables for theming.
+ * For light and dark mode themes separately, see the theme directory.
+ */
+
+:root {
+ /* ───────────────────────────────────────────────────────
+ Color Palette - Light Theme
+ - Primary: More muted/dimmed colors
+ - Secondary: Brighter, more vibrant colors
+ ─────────────────────────────────────────────────────── */
+
+ /* Neutral Colors */
+ --bk-color-black-primary: 0, 0%, 10%; /* #1A1A1A */
+ --bk-color-black-secondary: 0, 10%, 30%; /* #544545 */
+ --bk-color-white-primary: 0, 100%, 90%; /* #FFCCCC */
+ --bk-color-white-secondary: 0, 0%, 90%; /* #E6E6E6 */
+
+ /* Brand Colors */
+ --bk-color-pink-primary: 300, 60%, 40%; /* #A329A3 */
+ --bk-color-pink-secondary: 260, 100%, 70%; /* #9966FF */
+ --bk-color-purple-primary: 270, 60%, 40%; /* #6629A3 */
+ --bk-color-purple-secondary: 230, 100%, 70%; /* #6680FF */
+ --bk-color-blue-primary: 240, 60%, 40%; /* #2929A3 */
+ --bk-color-blue-secondary: 200, 100%, 70%; /* #66CCFF */
+
+ /* Supporting Colors */
+ --bk-color-cyan-primary: 200, 60%, 40%; /* #297AA3 */
+ --bk-color-cyan-secondary: 160, 100%, 70%; /* #66FFCC */
+ --bk-color-green-primary: 160, 60%, 40%; /* #29A37A */
+ --bk-color-green-secondary: 110, 100%, 70%; /* #80FF66 */
+ --bk-color-yellow-primary: 30, 60%, 40%; /* #A36629 */
+ --bk-color-yellow-secondary: 70, 100%, 70%; /* #A36629 */
+ --bk-color-orange-primary: 10, 60%, 40%; /* #A33D29 */
+ --bk-color-orange-secondary: 30, 100%, 70%; /* #FFB266 */
+ --bk-color-red-primary: 330, 60%, 40%; /* #A32966 */
+ --bk-color-red-secondary: 0, 100%, 70%; /* #FF6666 */
+
+ /* ───────────────────────────────────────────────────────
+ Component Theme Colors - Light
+ Using consistent alpha values:
+ - Line: 0.6 (borders)
+ - Flat: 0.2 (backgrounds)
+ - text: 0.9-1.0 (indicators)
+ ─────────────────────────────────────────────────────── */
+
+ /* Guide Component Theme */
+ --bk-guide-color-line-theme: hsla(var(--bk-color-blue-primary), 0.15);
+ --bk-guide-color-pattern-theme: hsla(var(--bk-color-purple-secondary), 0.2);
+ --bk-guide-color-auto-theme: hsla(var(--bk-color-blue-secondary), 0.15);
+ --bk-guide-color-fixed-theme: hsla(var(--bk-color-blue-secondary), 0.15);
+
+ /* Baseline Component Theme */
+ --bk-baseline-color-line-theme: hsla(var(--bk-color-blue-primary), 0.15);
+ --bk-baseline-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.2);
+
+ /* Spacer Component Theme */
+ --bk-spacer-color-line-theme: hsla(var(--bk-color-purple-primary), 0.6);
+ --bk-spacer-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.2);
+ --bk-spacer-color-text-theme: hsla(var(--bk-color-purple-primary), 1);
+
+ /* Box Component Theme */
+ --bk-box-color-line-theme: hsla(var(--bk-color-pink-primary), 0.6);
+ --bk-box-color-flat-theme: hsla(var(--bk-color-pink-secondary), 0.2);
+ --bk-box-color-text-theme: hsla(var(--bk-color-pink-primary), 0.9);
+
+ /* Stack Component Theme */
+ --bk-stack-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-stack-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.2);
+ --bk-stack-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ /* Layout Component Theme */
+ --bk-layout-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-layout-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.2);
+ --bk-layout-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ /* Padder Component Theme */
+ --bk-padder-color-theme: hsla(var(--bk-color-purple-primary), 0.6);
+}
+
+/* ───────────────────────────────────────────────────────
+ Dark Theme
+ - Inverts Primary/Secondary relationships
+ - Adjusts alpha values for better contrast
+─────────────────────────────────────────────────────── */
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ /* Neutral Colors - Dark */
+ --bk-color-black-primary: 0, 10%, 30%; /* #544545 */
+ --bk-color-black-secondary: 0, 0%, 10%; /* #1A1A1A */
+ --bk-color-white-primary: 0, 0%, 90%; /* #E6E6E6 */
+ --bk-color-white-secondary: 0, 100%, 90%; /* #FFCCCC */
+
+ /* Brand Colors - Dark */
+ --bk-color-pink-primary: 260, 100%, 70%; /* #9966FF */
+ --bk-color-pink-secondary: 300, 60%, 40%; /* #A329A3 */
+ --bk-color-purple-primary: 230, 100%, 70%; /* #6680FF */
+ --bk-color-purple-secondary: 270, 60%, 40%; /* #6629A3 */
+ --bk-color-blue-primary: 200, 100%, 70%; /* #66CCFF */
+ --bk-color-blue-secondary: 240, 60%, 40%; /* #2929A3 */
+
+ /* Supporting Colors - Dark */
+ --bk-color-cyan-primary: 160, 100%, 70%; /* #66FFCC */
+ --bk-color-cyan-secondary: 200, 60%, 40%; /* #297AA3 */
+ --bk-color-green-primary: 110, 100%, 70%; /* #80FF66 */
+ --bk-color-green-secondary: 160, 60%, 40%; /* #29A37A */
+ --bk-color-yellow-primary: 70, 100%, 70%; /* #A36629 */
+ --bk-color-yellow-secondary: 30, 60%, 40%; /* #A36629 */
+ --bk-color-orange-primary: 30, 100%, 70%; /* #FFB266 */
+ --bk-color-orange-secondary: 10, 60%, 40%; /* #A33D29 */
+ --bk-color-red-primary: 0, 100%, 70%; /* #FF6666 */
+ --bk-color-red-secondary: 330, 60%, 40%; /* #A32966 */
+
+ /* Component Theme Overrides - Dark */
+ --bk-baseline-color-line-theme: hsla(var(--bk-color-white-primary), 0.1);
+ --bk-baseline-color-flat-theme: hsla(var(--bk-color-white-secondary), 0.2);
+
+ --bk-box-color-line-theme: hsla(var(--bk-color-pink-primary), 0.6);
+ --bk-box-color-flat-theme: hsla(var(--bk-color-pink-secondary), 0.3);
+ --bk-box-color-text-theme: hsla(var(--bk-color-pink-primary), 0.9);
+
+ --bk-guide-color-line-theme: hsla(var(--bk-color-white-primary), 0.1);
+ --bk-guide-color-pattern-theme: hsla(var(--bk-color-purple-secondary), 0.25);
+ --bk-guide-color-auto-theme: hsla(var(--bk-color-blue-secondary), 0.2);
+ --bk-guide-color-fixed-theme: hsla(var(--bk-color-blue-secondary), 0.2);
+
+ --bk-stack-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-stack-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.3);
+ --bk-stack-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ --bk-layout-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-layout-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.3);
+ --bk-layout-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ --bk-spacer-color-line-theme: hsla(var(--bk-color-purple-primary), 0.7);
+ --bk-spacer-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.3);
+ --bk-spacer-color-text-theme: hsla(var(--bk-color-purple-primary), 1);
+ }
+}
diff --git a/src/components/styles/theme/dark.css b/src/components/styles/theme/dark.css
new file mode 100644
index 00000000..8408b08d
--- /dev/null
+++ b/src/components/styles/theme/dark.css
@@ -0,0 +1,67 @@
+/**
+ * Baseline Kit Dark Theme
+ *
+ * Organization:
+ * 1. Color Palette (HSL values)
+ * 2. Component Theme Colors
+ */
+
+:root {
+ /* ───────────────────────────────────────────────────────
+ Color Palette - Dark Theme
+ - Inverts Primary/Secondary relationships
+ - Adjusts values for better contrast
+ ─────────────────────────────────────────────────────── */
+
+ /* Neutral Colors - Dark */
+ --bk-color-black-primary: 0, 10%, 30%; /* #544545 */
+ --bk-color-black-secondary: 0, 0%, 10%; /* #1A1A1A */
+ --bk-color-white-primary: 0, 0%, 90%; /* #E6E6E6 */
+ --bk-color-white-secondary: 0, 100%, 90%; /* #FFCCCC */
+
+ /* Brand Colors - Dark */
+ --bk-color-pink-primary: 260, 100%, 70%; /* #9966FF */
+ --bk-color-pink-secondary: 300, 60%, 40%; /* #A329A3 */
+ --bk-color-purple-primary: 230, 100%, 70%; /* #6680FF */
+ --bk-color-purple-secondary: 270, 60%, 40%; /* #6629A3 */
+ --bk-color-blue-primary: 200, 100%, 70%; /* #66CCFF */
+ --bk-color-blue-secondary: 240, 60%, 40%; /* #2929A3 */
+
+ /* Supporting Colors - Dark */
+ --bk-color-cyan-primary: 160, 100%, 70%; /* #66FFCC */
+ --bk-color-cyan-secondary: 200, 60%, 40%; /* #297AA3 */
+ --bk-color-green-primary: 110, 100%, 70%; /* #80FF66 */
+ --bk-color-green-secondary: 160, 60%, 40%; /* #29A37A */
+ --bk-color-yellow-primary: 70, 100%, 70%; /* #A36629 */
+ --bk-color-yellow-secondary: 30, 60%, 40%; /* #A36629 */
+ --bk-color-orange-primary: 30, 100%, 70%; /* #FFB266 */
+ --bk-color-orange-secondary: 10, 60%, 40%; /* #A33D29 */
+ --bk-color-red-primary: 0, 100%, 70%; /* #FF6666 */
+ --bk-color-red-secondary: 330, 60%, 40%; /* #A32966 */
+
+ /* ───────────────────────────────────────────────────────
+ Component Theme Colors - Dark
+ - Adjusts alpha values for better contrast
+ ─────────────────────────────────────────────────────── */
+
+ /* Component Theme Overrides - Dark */
+ --bk-baseline-color-line-theme: hsla(var(--bk-color-white-primary), 0.1);
+ --bk-baseline-color-flat-theme: hsla(var(--bk-color-white-secondary), 0.2);
+
+ --bk-box-color-line-theme: hsla(var(--bk-color-pink-primary), 0.6);
+ --bk-box-color-flat-theme: hsla(var(--bk-color-pink-secondary), 0.3);
+ --bk-box-color-text-theme: hsla(var(--bk-color-pink-primary), 0.9);
+
+ --bk-guide-color-line-theme: hsla(var(--bk-color-white-primary), 0.1);
+ --bk-guide-color-pattern-theme: hsla(var(--bk-color-purple-secondary), 0.25);
+ --bk-guide-color-auto-theme: hsla(var(--bk-color-blue-secondary), 0.2);
+ --bk-guide-color-fixed-theme: hsla(var(--bk-color-blue-secondary), 0.2);
+
+ --bk-stack-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-stack-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.3);
+ --bk-stack-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ --bk-layout-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-layout-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.3);
+ --bk-layout-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+}
diff --git a/src/components/styles/theme/default.css b/src/components/styles/theme/default.css
new file mode 100644
index 00000000..ad358a0a
--- /dev/null
+++ b/src/components/styles/theme/default.css
@@ -0,0 +1,82 @@
+/**
+ * Baseline Kit Default Theme (Light)
+ *
+ * Organization:
+ * 1. Color Palette (HSL values)
+ * 2. Component Theme Colors
+ */
+
+:root {
+ /* ───────────────────────────────────────────────────────
+ Color Palette - Light Theme
+ - Primary: More muted/dimmed colors
+ - Secondary: Brighter, more vibrant colors
+ ─────────────────────────────────────────────────────── */
+
+ /* Neutral Colors */
+ --bk-color-black-primary: 0, 0%, 10%; /* #1A1A1A */
+ --bk-color-black-secondary: 0, 10%, 30%; /* #544545 */
+ --bk-color-white-primary: 0, 100%, 90%; /* #FFCCCC */
+ --bk-color-white-secondary: 0, 0%, 90%; /* #E6E6E6 */
+
+ /* Brand Colors */
+ --bk-color-pink-primary: 300, 60%, 40%; /* #A329A3 */
+ --bk-color-pink-secondary: 260, 100%, 70%; /* #9966FF */
+ --bk-color-purple-primary: 270, 60%, 40%; /* #6629A3 */
+ --bk-color-purple-secondary: 230, 100%, 70%; /* #6680FF */
+ --bk-color-blue-primary: 240, 60%, 40%; /* #2929A3 */
+ --bk-color-blue-secondary: 200, 100%, 70%; /* #66CCFF */
+
+ /* Supporting Colors */
+ --bk-color-cyan-primary: 200, 60%, 40%; /* #297AA3 */
+ --bk-color-cyan-secondary: 160, 100%, 70%; /* #66FFCC */
+ --bk-color-green-primary: 160, 60%, 40%; /* #29A37A */
+ --bk-color-green-secondary: 110, 100%, 70%; /* #80FF66 */
+ --bk-color-yellow-primary: 30, 60%, 40%; /* #A36629 */
+ --bk-color-yellow-secondary: 70, 100%, 70%; /* #A36629 */
+ --bk-color-orange-primary: 10, 60%, 40%; /* #A33D29 */
+ --bk-color-orange-secondary: 30, 100%, 70%; /* #FFB266 */
+ --bk-color-red-primary: 330, 60%, 40%; /* #A32966 */
+ --bk-color-red-secondary: 0, 100%, 70%; /* #FF6666 */
+
+ /* ───────────────────────────────────────────────────────
+ Component Theme Colors - Light
+ Using consistent alpha values:
+ - Line: 0.6 (borders)
+ - Flat: 0.2 (backgrounds)
+ - text: 0.9-1.0 (indicators)
+ ─────────────────────────────────────────────────────── */
+
+ /* Guide Component Theme */
+ --bk-guide-color-line-theme: hsla(var(--bk-color-blue-primary), 0.15);
+ --bk-guide-color-pattern-theme: hsla(var(--bk-color-purple-secondary), 0.2);
+ --bk-guide-color-auto-theme: hsla(var(--bk-color-blue-secondary), 0.15);
+ --bk-guide-color-fixed-theme: hsla(var(--bk-color-blue-secondary), 0.15);
+
+ /* Baseline Component Theme */
+ --bk-baseline-color-line-theme: hsla(var(--bk-color-blue-primary), 0.15);
+ --bk-baseline-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.2);
+
+ /* Spacer Component Theme */
+ --bk-spacer-color-line-theme: hsla(var(--bk-color-purple-primary), 0.6);
+ --bk-spacer-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.2);
+ --bk-spacer-color-text-theme: hsla(var(--bk-color-purple-primary), 1);
+
+ /* Box Component Theme */
+ --bk-box-color-line-theme: hsla(var(--bk-color-pink-primary), 0.6);
+ --bk-box-color-flat-theme: hsla(var(--bk-color-pink-secondary), 0.2);
+ --bk-box-color-text-theme: hsla(var(--bk-color-pink-primary), 0.9);
+
+ /* Stack Component Theme */
+ --bk-stack-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-stack-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.2);
+ --bk-stack-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ /* Layout Component Theme */
+ --bk-layout-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
+ --bk-layout-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.2);
+ --bk-layout-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
+
+ /* Padder Component Theme */
+ --bk-padder-color-theme: hsla(var(--bk-color-purple-primary), 0.6);
+}
diff --git a/src/components/styles/theme/tokens.css b/src/components/styles/theme/tokens.css
new file mode 100644
index 00000000..43fc709c
--- /dev/null
+++ b/src/components/styles/theme/tokens.css
@@ -0,0 +1,82 @@
+/**
+ * Baseline Kit Theme Tokens Template
+ *
+ * Use this file as a starting point for creating your own custom theme.
+ * This file contains all theme variables but no values.
+ *
+ * To create a custom theme:
+ * 1. Copy this file
+ * 2. Fill in your custom values
+ * 3. Import it in your application after the core styles
+ */
+
+:root {
+ /* ───────────────────────────────────────────────────────
+ Color Palette
+ Provide HSL values in the format: hue, saturation%, lightness%
+ ─────────────────────────────────────────────────────── */
+
+ /* Neutral Colors */
+ --bk-color-black-primary: ;
+ --bk-color-black-secondary: ;
+ --bk-color-white-primary: ;
+ --bk-color-white-secondary: ;
+
+ /* Brand Colors */
+ --bk-color-pink-primary: ;
+ --bk-color-pink-secondary: ;
+ --bk-color-purple-primary: ;
+ --bk-color-purple-secondary: ;
+ --bk-color-blue-primary: ;
+ --bk-color-blue-secondary: ;
+
+ /* Supporting Colors */
+ --bk-color-cyan-primary: ;
+ --bk-color-cyan-secondary: ;
+ --bk-color-green-primary: ;
+ --bk-color-green-secondary: ;
+ --bk-color-yellow-primary: ;
+ --bk-color-yellow-secondary: ;
+ --bk-color-orange-primary: ;
+ --bk-color-orange-secondary: ;
+ --bk-color-red-primary: ;
+ --bk-color-red-secondary: ;
+
+ /* ───────────────────────────────────────────────────────
+ Component Theme Colors
+ Use hsla(var(--color-variable), alpha) format for transparency
+ ─────────────────────────────────────────────────────── */
+
+ /* Guide Component Theme */
+ --bk-guide-color-line-theme: ;
+ --bk-guide-color-pattern-theme: ;
+ --bk-guide-color-auto-theme: ;
+ --bk-guide-color-fixed-theme: ;
+
+ /* Baseline Component Theme */
+ --bk-baseline-color-line-theme: ;
+ --bk-baseline-color-flat-theme: ;
+
+ /* Spacer Component Theme */
+ --bk-spacer-color-line-theme: ;
+ --bk-spacer-color-flat-theme: ;
+ --bk-spacer-color-text-theme: ;
+
+ /* Box Component Theme */
+ --bk-box-color-line-theme: ;
+ --bk-box-color-flat-theme: ;
+ --bk-box-color-text-theme: ;
+
+ /* Stack Component Theme */
+ --bk-stack-color-line-theme: ;
+ --bk-stack-color-flat-theme: ;
+ --bk-stack-color-text-theme: ;
+
+ /* Layout Component Theme */
+ --bk-layout-color-line-theme: ;
+ --bk-layout-color-flat-theme: ;
+ --bk-layout-color-text-theme: ;
+
+ /* Padder Component Theme */
+ --bk-padder-color-theme: ;
+}
diff --git a/src/components/theme/theme.css b/src/components/theme/theme.css
deleted file mode 100644
index bf941e84..00000000
--- a/src/components/theme/theme.css
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * Baseline Kit Theme System
- *
- * Organization:
- * 1. Color Palette (HSL values)
- * 2. Component Theme Colors
- * 3. Dark Mode Overrides
- */
-
-:root {
- /* ───────────────────────────────────────────────────────
- Color Palette - Light Theme
- - Primary: More muted/dimmed colors
- - Secondary: Brighter, more vibrant colors
- ─────────────────────────────────────────────────────── */
-
- /* Neutral Colors */
- --bk-color-black-primary: 0, 0%, 10%; /* #1A1A1A */
- --bk-color-black-secondary: 0, 10%, 30%; /* #544545 */
- --bk-color-white-primary: 0, 100%, 90%; /* #FFCCCC */
- --bk-color-white-secondary: 0, 0%, 90%; /* #E6E6E6 */
-
- /* Brand Colors */
- --bk-color-pink-primary: 300, 60%, 40%; /* #A329A3 */
- --bk-color-pink-secondary: 260, 100%, 70%; /* #9966FF */
- --bk-color-purple-primary: 270, 60%, 40%; /* #6629A3 */
- --bk-color-purple-secondary: 230, 100%, 70%; /* #6680FF */
- --bk-color-blue-primary: 240, 60%, 40%; /* #2929A3 */
- --bk-color-blue-secondary: 200, 100%, 70%; /* #66CCFF */
-
- /* Supporting Colors */
- --bk-color-cyan-primary: 200, 60%, 40%; /* #297AA3 */
- --bk-color-cyan-secondary: 160, 100%, 70%; /* #66FFCC */
- --bk-color-green-primary: 160, 60%, 40%; /* #29A37A */
- --bk-color-green-secondary: 110, 100%, 70%; /* #80FF66 */
- --bk-color-yellow-primary: 30, 60%, 40%; /* #A36629 */
- --bk-color-yellow-secondary: 70, 100%, 70%; /* #A36629 */
- --bk-color-orange-primary: 10, 60%, 40%; /* #A33D29 */
- --bk-color-orange-secondary: 30, 100%, 70%; /* #FFB266 */
- --bk-color-red-primary: 330, 60%, 40%; /* #A32966 */
- --bk-color-red-secondary: 0, 100%, 70%; /* #FF6666 */
-
- /* ───────────────────────────────────────────────────────
- Component Theme Colors - Light
- Using consistent alpha values:
- - Line: 0.6 (borders)
- - Flat: 0.2 (backgrounds)
- - text: 0.9-1.0 (indicators)
- ─────────────────────────────────────────────────────── */
-
- /* Guide Component Theme */
- --bk-guide-color-line-theme: hsla(var(--bk-color-blue-primary), 0.15);
- --bk-guide-color-pattern-theme: hsla(var(--bk-color-purple-secondary), 0.2);
- --bk-guide-color-auto-theme: hsla(var(--bk-color-blue-secondary), 0.15);
- --bk-guide-color-fixed-theme: hsla(var(--bk-color-blue-secondary), 0.15);
-
- /* Baseline Component Theme */
- --bk-baseline-color-line-theme: hsla(var(--bk-color-blue-primary), 0.15);
- --bk-baseline-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.2);
-
- /* Spacer Component Theme */
- --bk-spacer-color-line-theme: hsla(var(--bk-color-purple-primary), 0.6);
- --bk-spacer-color-flat-theme: hsla(var(--bk-color-purple-secondary), 0.2);
- --bk-spacer-color-text-theme: hsla(var(--bk-color-purple-primary), 1);
-
- /* Box Component Theme */
- --bk-box-color-line-theme: hsla(var(--bk-color-pink-primary), 0.6);
- --bk-box-color-flat-theme: hsla(var(--bk-color-pink-secondary), 0.2);
- --bk-box-color-text-theme: hsla(var(--bk-color-pink-primary), 0.9);
-
- /* Stack Component Theme */
- --bk-stack-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
- --bk-stack-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.2);
- --bk-stack-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
-
- /* Layout Component Theme */
- --bk-layout-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
- --bk-layout-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.2);
- --bk-layout-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
-
- /* Padder Component Theme */
- --bk-padder-color-theme: hsla(var(--bk-color-purple-primary), 0.6);
-}
-
-/* ───────────────────────────────────────────────────────
- Dark Theme
- - Inverts Primary/Secondary relationships
- - Adjusts alpha values for better contrast
-─────────────────────────────────────────────────────── */
-
-@media (prefers-color-scheme: dark) {
- :root {
- /* Neutral Colors - Dark */
- --bk-color-black-primary: 0, 10%, 30%; /* #544545 */
- --bk-color-black-secondary: 0, 0%, 10%; /* #1A1A1A */
- --bk-color-white-primary: 0, 0%, 90%; /* #E6E6E6 */
- --bk-color-white-secondary: 0, 100%, 90%; /* #FFCCCC */
-
- /* Brand Colors - Dark */
- --bk-color-pink-primary: 260, 100%, 70%; /* #9966FF */
- --bk-color-pink-secondary: 300, 60%, 40%; /* #A329A3 */
- --bk-color-purple-primary: 230, 100%, 70%; /* #6680FF */
- --bk-color-purple-secondary: 270, 60%, 40%; /* #6629A3 */
- --bk-color-blue-primary: 200, 100%, 70%; /* #66CCFF */
- --bk-color-blue-secondary: 240, 60%, 40%; /* #2929A3 */
-
- /* Supporting Colors - Dark */
- --bk-color-cyan-primary: 160, 100%, 70%; /* #66FFCC */
- --bk-color-cyan-secondary: 200, 60%, 40%; /* #297AA3 */
- --bk-color-green-primary: 110, 100%, 70%; /* #80FF66 */
- --bk-color-green-secondary: 160, 60%, 40%; /* #29A37A */
- --bk-color-yellow-primary: 70, 100%, 70%; /* #A36629 */
- --bk-color-yellow-secondary: 30, 60%, 40%; /* #A36629 */
- --bk-color-orange-primary: 30, 100%, 70%; /* #FFB266 */
- --bk-color-orange-secondary: 10, 60%, 40%; /* #A33D29 */
- --bk-color-red-primary: 0, 100%, 70%; /* #FF6666 */
- --bk-color-red-secondary: 330, 60%, 40%; /* #A32966 */
-
- /* Component Theme Overrides - Dark */
- --bk-baseline-color-line-theme: hsla(var(--bk-color-white-primary), 0.1);
- --bk-baseline-color-flat-theme: hsla(var(--bk-color-white-secondary), 0.2);
-
- --bk-box-color-line-theme: hsla(var(--bk-color-pink-primary), 0.6);
- --bk-box-color-flat-theme: hsla(var(--bk-color-pink-secondary), 0.3);
- --bk-box-color-text-theme: hsla(var(--bk-color-pink-primary), 0.9);
-
- --bk-guide-color-line-theme: hsla(var(--bk-color-white-primary), 0.1);
- --bk-guide-color-pattern-theme: hsla(var(--bk-color-purple-secondary), 0.25);
- --bk-guide-color-auto-theme: hsla(var(--bk-color-blue-secondary), 0.2);
- --bk-guide-color-fixed-theme: hsla(var(--bk-color-blue-secondary), 0.2);
-
- --bk-stack-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
- --bk-stack-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.3);
- --bk-stack-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
-
- --bk-layout-color-line-theme: hsla(var(--bk-color-red-primary), 0.6);
- --bk-layout-color-flat-theme: hsla(var(--bk-color-red-secondary), 0.3);
- --bk-layout-color-text-theme: hsla(var(--bk-color-red-primary), 0.9);
- }
-}
\ No newline at end of file
diff --git a/src/components/types.ts b/src/components/types.ts
index 4c5f2541..95f96395 100644
--- a/src/components/types.ts
+++ b/src/components/types.ts
@@ -44,28 +44,28 @@ export type Spacing =
*/
export type PaddingValue =
| number
- | [number, number] // [block, inline]
- | [number, number, number, number] // [top, right, bottom, left]
+ | [number, number] // [block, inline]
+ | [number, number, number, number] // [top, right, bottom, left]
| {
- top?: number;
- bottom?: number;
- left?: number;
- right?: number;
-}
+ top?: number
+ bottom?: number
+ left?: number
+ right?: number
+ }
/** Resolved padding values for all edges after normalization. */
export type Padding = {
- top: number;
- right: number;
- bottom: number;
- left: number;
+ top: number
+ right: number
+ bottom: number
+ left: number
}
/** Props interface for components that support spacing configuration. */
export type SpacingProps = {
- padding?: PaddingValue;
- block?: Spacing;
- inline?: Spacing;
+ padding?: PaddingValue
+ block?: Spacing
+ inline?: Spacing
}
// Grid Types ------------------------------------------------------------------
@@ -86,11 +86,11 @@ export type GuideColumnsPattern = readonly GuideColumnValue[]
/** Valid grid alignment values. */
export const GRID_ALIGNMENTS = ['start', 'center', 'end'] as const
-export type GridAlignment = typeof GRID_ALIGNMENTS[number]
+export type GridAlignment = (typeof GRID_ALIGNMENTS)[number]
/** Valid component variants affecting visual style. */
export const PADD_VARIANTS = ['line', 'flat'] as const
-export type PaddedVariant = typeof PADD_VARIANTS[number]
+export type PaddedVariant = (typeof PADD_VARIANTS)[number]
// Component Base Types --------------------------------------------------------
@@ -99,29 +99,31 @@ export type PaddedVariant = typeof PADD_VARIANTS[number]
* Provides consistent sizing, spacing, styling, and debugging options.
*/
export type ComponentsProps = {
- debugging?: DebuggingMode;
- className?: string;
- style?: React.CSSProperties;
- height?: React.CSSProperties['height'];
- width?: React.CSSProperties['width'];
+ debugging?: DebuggingMode
+ className?: string
+ style?: React.CSSProperties
+ height?: React.CSSProperties['height']
+ width?: React.CSSProperties['width']
} & SpacingProps
/** Base configuration for components that support padding. */
export type PaddedBaseConfig = {
- base?: number;
- color?: React.CSSProperties['color'] | React.CSSProperties['backgroundColor'];
- zIndex?: React.CSSProperties['zIndex'];
+ base?: number
+ color?: React.CSSProperties['color'] | React.CSSProperties['backgroundColor']
+ zIndex?: React.CSSProperties['zIndex']
}
export type Variant = 'line' | 'flat' | 'pattern'
-export type Gaps = {
- gap?: React.CSSProperties['gap']
- rowGap?: never
- columnGap?: never
-} | {
- /** When using separate gaps, omit unified gap */
- gap?: never
- rowGap?: React.CSSProperties['rowGap']
- columnGap?: React.CSSProperties['columnGap']
-}
+export type Gaps =
+ | {
+ gap?: React.CSSProperties['gap']
+ rowGap?: never
+ columnGap?: never
+ }
+ | {
+ /** When using separate gaps, omit unified gap */
+ gap?: never
+ rowGap?: React.CSSProperties['rowGap']
+ columnGap?: React.CSSProperties['columnGap']
+ }
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index e0466fde..e3b4e1aa 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -14,4 +14,7 @@ export * from './useGuide'
// Configuration
export * from './useConfig'
-export * from './useDebug'
\ No newline at end of file
+export * from './useDebug'
+
+// Don't export useIsClient as it's only used internally
+// export * from './useIsClient'
diff --git a/src/hooks/useBaseline.ts b/src/hooks/useBaseline.ts
index 9daaf33b..9eaf0f61 100644
--- a/src/hooks/useBaseline.ts
+++ b/src/hooks/useBaseline.ts
@@ -66,7 +66,7 @@ export function useBaseline(
snapping = 'none',
spacing = {},
warnOnMisalignment = false,
- }: BaselineOptions = {},
+ }: BaselineOptions = {}
): BaselineResult {
if (base < 1) {
throw new Error('Base must be >= 1 for baseline alignment.')
@@ -83,10 +83,14 @@ export function useBaseline(
const isAligned = height % base === 0
// Log the warning only once
- if (!isAligned && warnOnMisalignment && process.env.NODE_ENV === 'development') {
+ if (
+ !isAligned &&
+ warnOnMisalignment &&
+ process.env.NODE_ENV === 'development'
+ ) {
if (!hasWarnedRef.current) {
console.warn(
- `[useBaseline] Element height (${height}px) is not aligned with base (${base}px).`,
+ `[useBaseline] Element height (${height}px) is not aligned with base (${base}px).`
)
hasWarnedRef.current = true // Mark warning as logged
}
@@ -104,7 +108,12 @@ export function useBaseline(
}
// Snap exactly once.
- const finalPadding = calculateSnappedSpacing(height, base, initialPadding, snapping)
+ const finalPadding = calculateSnappedSpacing(
+ height,
+ base,
+ initialPadding,
+ snapping
+ )
didSnapRef.current = true
return { padding: finalPadding, isAligned, height }
diff --git a/src/hooks/useConfig.ts b/src/hooks/useConfig.ts
index b70ef933..07b279a0 100644
--- a/src/hooks/useConfig.ts
+++ b/src/hooks/useConfig.ts
@@ -1,8 +1,8 @@
import { useMemo } from 'react'
-import { Config, useDefaultConfig } from '@components'
+import { ConfigSchema, useDefaultConfig } from '@components'
/** Type helper that merges base configuration with component-specific settings. */
-export type ComponentConfig
= Config[K] & {
+export type ComponentConfig = ConfigSchema[K] & {
/** Base unit for spacing calculations */
base: number
}
@@ -41,12 +41,14 @@ export type ComponentConfig = Config[K] & {
* }
* ```
*/
-export function useConfig(component: K): ComponentConfig {
+export function useConfig(
+ component: K
+): ComponentConfig {
const defaultConfig = useDefaultConfig()
return useMemo(() => {
return Object.assign(
{ base: defaultConfig.base },
- defaultConfig[component],
+ defaultConfig[component]
) as ComponentConfig
}, [defaultConfig, component])
-}
\ No newline at end of file
+}
diff --git a/src/hooks/useDebug.ts b/src/hooks/useDebug.ts
index 8679b379..15c1d148 100644
--- a/src/hooks/useDebug.ts
+++ b/src/hooks/useDebug.ts
@@ -3,13 +3,13 @@ import { DebuggingMode } from '../components/Config/Config'
interface DebugResult {
/** Whether debug visuals should be shown */
- isShown: boolean;
+ isShown: boolean
/** Whether debug features exist but are hidden */
- isHidden: boolean;
+ isHidden: boolean
/** Whether debug features are disabled */
- isNone: boolean;
+ isNone: boolean
/** Current debugging mode */
- debugging: DebuggingMode | undefined;
+ debugging: DebuggingMode | undefined
}
/**
@@ -50,7 +50,7 @@ interface DebugResult {
*/
export function useDebug(
debuggingProp?: DebuggingMode,
- debuggingConfig?: DebuggingMode,
+ debuggingConfig?: DebuggingMode
): DebugResult {
return useMemo(() => {
const effective = debuggingProp ?? debuggingConfig
@@ -61,4 +61,4 @@ export function useDebug(
debugging: effective,
}
}, [debuggingProp, debuggingConfig])
-}
\ No newline at end of file
+}
diff --git a/src/hooks/useGuide.ts b/src/hooks/useGuide.ts
index 13205f19..23a49e75 100644
--- a/src/hooks/useGuide.ts
+++ b/src/hooks/useGuide.ts
@@ -1,17 +1,18 @@
import * as React from 'react'
-import { GuideConfig, GuideColumnsPattern, isValidGuidePattern } from '@components'
+import { GuideConfig, GuideColumnsPattern } from '@components'
+import { isValidGuidePattern } from '@/components/Guide/validation'
import { formatValue, convertValue, normalizeValue } from '@utils'
import { useMeasure } from './useMeasure'
export interface GuideResult {
/** CSS grid template string */
- template: string;
+ template: string
/** Total number of columns */
- columnsCount: number;
+ columnsCount: number
/** Final gap size in pixels */
- calculatedGap: number;
+ calculatedGap: number
/** Whether the configuration is valid */
- isValid: boolean;
+ isValid: boolean
}
/**
@@ -64,7 +65,7 @@ export interface GuideResult {
*/
export function useGuide(
ref: React.RefObject,
- config: GuideConfig,
+ config: GuideConfig
): GuideResult {
const { width } = useMeasure(ref)
const hasWarnedRef = React.useRef(false) // Track if warning has been logged
@@ -73,7 +74,9 @@ export function useGuide(
// Default values
const variant = config.variant ?? 'line'
const base = config.base ?? 8
- // Normalize the gap using the base value
+
+ // The gap is already adjusted in createGridConfig for line variants
+ // So we should use config.gap directly without further adjustment
const gap = normalizeValue(config.gap ?? 0, { base })
// Return invalid result if no width
@@ -88,106 +91,132 @@ export function useGuide(
try {
switch (variant) {
- case 'line': {
- // Simple vertical lines
- const columns = Math.max(1, Math.floor(width / (gap + 1)) + 1)
- return {
- template: `repeat(${columns}, 1px)`,
- columnsCount: columns,
- calculatedGap: gap,
- isValid: true,
- }
- }
+ case 'line': {
+ // Simple vertical lines
+ // Account for -1px reduction in each gap when gap > 0
+ const finalGap = gap === 0 ? 0 : gap - 1
+ const actualGapWithLine = finalGap + 1
- case 'pattern': {
- // Custom column pattern
- if (!isValidGuidePattern(config.columns)) {
- throw new Error('Invalid "pattern" columns array')
- }
- const columnsArr = (config.columns as GuideColumnsPattern).map(col => {
- if (typeof col === 'number') return `${col}px`
- return col
- })
+ let columns: number
+
+ if (finalGap === 0) {
+ // When gap is 0, need a column per pixel + 1
+ columns = Math.max(1, Math.floor(width) + 1)
+ } else {
+ // For line variant with gap > 0:
+ // Account for the -1px reduction in each gap
+ columns = Math.max(
+ 1,
+ Math.floor((width + 1) / actualGapWithLine) + 1
+ )
+ }
- // Validate no zero widths
- if (columnsArr.some(c => c === '0' || c === '0px')) {
return {
- template: 'none',
- columnsCount: 0,
- calculatedGap: 0,
- isValid: false,
+ template: `repeat(${columns}, 1px)`,
+ columnsCount: columns,
+ calculatedGap: gap > 0 ? gap - 1 : gap,
+ isValid: true,
}
}
- return {
- template: columnsArr.join(' '),
- columnsCount: columnsArr.length,
- calculatedGap: gap,
- isValid: true,
- }
- }
+ case 'pattern': {
+ // Custom column pattern
+ if (!config.columns || !Array.isArray(config.columns)) {
+ throw new Error('Missing or invalid pattern columns')
+ }
- case 'fixed': {
- // Fixed number of columns
- const colCount = typeof config.columns === 'number' ? config.columns : 0
- if (colCount < 1) {
- throw new Error(`Invalid columns count: ${colCount}`)
- }
- const colWidth = config.columnWidth
- ? formatValue(config.columnWidth)
- : '1fr'
-
- return {
- template: `repeat(${colCount}, ${colWidth})`,
- columnsCount: colCount,
- calculatedGap: gap,
- isValid: true,
- }
- }
+ if (!isValidGuidePattern(config.columns)) {
+ throw new Error('Invalid "pattern" columns array')
+ }
+
+ const columnsArr = (config.columns as GuideColumnsPattern).map(
+ (col) => {
+ if (typeof col === 'number') return `${col}px`
+ return col
+ }
+ )
+
+ // Validate no zero widths
+ if (columnsArr.some((c) => c === '0' || c === '0px')) {
+ return {
+ template: 'none',
+ columnsCount: 0,
+ calculatedGap: 0,
+ isValid: false,
+ }
+ }
- case 'auto': {
- // Auto-fitting columns
- const colWidth = config.columnWidth ?? 'auto'
- if (colWidth === 'auto') {
return {
- template: 'repeat(auto-fit, minmax(0, 1fr))',
- columnsCount: 1,
+ template: columnsArr.join(' '),
+ columnsCount: columnsArr.length,
calculatedGap: gap,
isValid: true,
}
}
- const colWidthStr =
- typeof colWidth === 'number' ? `${colWidth}px` : colWidth.toString()
-
- const pxVal = convertValue(colWidthStr) ?? 0
- const columns = pxVal > 0
- ? Math.max(1, Math.floor((width + gap) / (pxVal + gap)))
- : 1
-
- return {
- template: `repeat(auto-fit, minmax(${colWidthStr}, 1fr))`,
- columnsCount: columns,
- calculatedGap: gap,
- isValid: true,
+
+ case 'fixed': {
+ // Fixed number of columns
+ const colCount =
+ typeof config.columns === 'number' ? config.columns : 0
+ if (colCount < 1) {
+ throw new Error(`Invalid columns count: ${colCount}`)
+ }
+ const colWidth = config.columnWidth
+ ? formatValue(config.columnWidth)
+ : '1fr'
+
+ return {
+ template: `repeat(${colCount}, ${colWidth})`,
+ columnsCount: colCount,
+ calculatedGap: gap,
+ isValid: true,
+ }
}
- }
- default: {
- if (!hasWarnedRef.current) {
- console.warn(
- `[useGuide] Unknown variant "${variant}". Falling back to "line".`,
- )
- hasWarnedRef.current = true // Mark warning as logged
+ case 'auto': {
+ // Auto-filling columns
+ const colWidth = config.columnWidth ?? 'auto'
+ if (colWidth === 'auto') {
+ return {
+ template: 'repeat(auto-fill, minmax(0, 1fr))',
+ columnsCount: 1,
+ calculatedGap: gap,
+ isValid: true,
+ }
+ }
+ const colWidthStr =
+ typeof colWidth === 'number' ? `${colWidth}px` : colWidth.toString()
+
+ const pxVal = convertValue(colWidthStr) ?? 0
+ const columns =
+ pxVal > 0
+ ? Math.max(1, Math.floor((width + gap) / (pxVal + gap)))
+ : 1
+
+ return {
+ template: `repeat(auto-fill, ${colWidthStr})`,
+ columnsCount: columns,
+ calculatedGap: gap,
+ isValid: true,
+ }
}
- const columns = Math.max(1, Math.floor(width / (gap + 1)) + 1)
- return {
- template: `repeat(${columns}, 1px)`,
- columnsCount: columns,
- calculatedGap: gap,
- isValid: true,
+
+ default: {
+ if (!hasWarnedRef.current) {
+ console.warn(
+ `[useGuide] Unknown variant "${variant}". Falling back to "line".`
+ )
+ hasWarnedRef.current = true // Mark warning as logged
+ }
+ const columns = Math.max(1, Math.floor(width / (gap + 1)) + 1)
+ return {
+ template: `repeat(${columns}, 1px)`,
+ columnsCount: columns,
+ calculatedGap: gap,
+ isValid: true,
+ }
}
}
- }
} catch (error) {
console.warn('Error in useGuide:', error)
return {
diff --git a/src/hooks/useIsClient.ts b/src/hooks/useIsClient.ts
new file mode 100644
index 00000000..99e5472d
--- /dev/null
+++ b/src/hooks/useIsClient.ts
@@ -0,0 +1,18 @@
+import { useEffect, useState } from 'react'
+
+/**
+ * Hook to determine if the code is running on the client side.
+ *
+ * @returns Boolean indicating if the code is running in a browser environment
+ */
+export function useIsClient() {
+ // Start with false for SSR
+ const [isClient, setIsClient] = useState(false)
+
+ // Only run once on mount
+ useEffect(() => {
+ setIsClient(true)
+ }, [])
+
+ return isClient
+}
diff --git a/src/hooks/useMeasure.ts b/src/hooks/useMeasure.ts
index d1dbcce4..6d6f8c2f 100644
--- a/src/hooks/useMeasure.ts
+++ b/src/hooks/useMeasure.ts
@@ -3,11 +3,11 @@ import { rafThrottle } from '@utils'
export interface MeasureResult {
/** Measured width in pixels */
- width: number;
+ width: number
/** Measured height in pixels */
- height: number;
+ height: number
/** Function to force a remeasurement */
- refresh: () => void;
+ refresh: () => void
}
/**
@@ -35,7 +35,9 @@ export interface MeasureResult {
* }
* ```
*/
-export function useMeasure(ref: React.RefObject): MeasureResult {
+export function useMeasure(
+ ref: React.RefObject
+): MeasureResult {
const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 })
// measure() reads getBoundingClientRect() from ref.current
@@ -47,8 +49,8 @@ export function useMeasure(ref: React.RefObject): MeasureRes
width: rect ? Math.round(rect.width) : 0,
height: rect ? Math.round(rect.height) : 0,
}
- setDimensions(prev =>
- prev.width === next.width && prev.height === next.height ? prev : next,
+ setDimensions((prev) =>
+ prev.width === next.width && prev.height === next.height ? prev : next
)
} catch {
setDimensions({ width: 0, height: 0 })
@@ -59,11 +61,14 @@ export function useMeasure(ref: React.RefObject): MeasureRes
const refresh = React.useMemo(() => rafThrottle(measure), [measure])
// On mount (or when ref changes) perform an immediate measurement.
- React.useLayoutEffect(() => { measure() }, [measure])
+ React.useLayoutEffect(() => {
+ if (typeof window === 'undefined') return
+ measure()
+ }, [measure])
// Set up a ResizeObserver on mount.
React.useLayoutEffect(() => {
- if (!ref.current) return
+ if (typeof window === 'undefined' || !ref.current) return
const observer = new ResizeObserver(() => {
refresh()
})
diff --git a/src/hooks/useVirtual.ts b/src/hooks/useVirtual.ts
index fcd83737..7d739106 100644
--- a/src/hooks/useVirtual.ts
+++ b/src/hooks/useVirtual.ts
@@ -1,15 +1,21 @@
-import { RefObject, useCallback, useLayoutEffect, useMemo, useState } from 'react'
+import {
+ RefObject,
+ useCallback,
+ useLayoutEffect,
+ useMemo,
+ useState,
+} from 'react'
import { rafThrottle } from '@utils'
type VirtualResult = {
/** Total number of items/lines to virtualize */
- totalLines: number;
+ totalLines: number
/** Height of each item in pixels */
- lineHeight: number;
+ lineHeight: number
/** Reference to the scrollable container */
- containerRef: RefObject;
+ containerRef: RefObject
/** Additional items to render above/below viewport */
- buffer?: number | string;
+ buffer?: number | string
}
/**
@@ -63,9 +69,10 @@ export function useVirtual({
buffer = 0,
}: VirtualResult) {
// Convert buffer to numeric value
- const numericBuffer = useMemo(() =>
- typeof buffer === 'number' ? buffer : parseInt(buffer, 10) || 0
- , [buffer])
+ const numericBuffer = useMemo(
+ () => (typeof buffer === 'number' ? buffer : parseInt(buffer, 10) || 0),
+ [buffer]
+ )
/**
* Calculates the visible range of items based on scroll position
@@ -99,23 +106,27 @@ export function useVirtual({
updateRangeThrottled()
})
-
// Throttle updates for performance
const updateRange = useCallback(() => {
- setVisibleRange(prev => {
+ setVisibleRange((prev) => {
const next = calculateRange()
return prev.start !== next.start || prev.end !== next.end ? next : prev
})
}, [calculateRange])
- const updateRangeThrottled = useMemo(() => rafThrottle(updateRange), [updateRange])
+ const updateRangeThrottled = useMemo(
+ () => rafThrottle(updateRange),
+ [updateRange]
+ )
useLayoutEffect(() => {
const element = containerRef.current
if (!element) return
// Use IntersectionObserver for visibility tracking
- const observer = new IntersectionObserver(updateRangeThrottled, { threshold: 0 })
+ const observer = new IntersectionObserver(updateRangeThrottled, {
+ threshold: 0,
+ })
observer.observe(element)
updateRangeThrottled()
@@ -133,8 +144,8 @@ function useWindowEvents(events: string[], handler: () => void) {
useLayoutEffect(() => {
const wrappedHandler = () => stableHandler()
- events.forEach(evt => window.addEventListener(evt, wrappedHandler))
- return () => events.forEach(evt => window.removeEventListener(evt, wrappedHandler))
+ events.forEach((evt) => window.addEventListener(evt, wrappedHandler))
+ return () =>
+ events.forEach((evt) => window.removeEventListener(evt, wrappedHandler))
}, [events, stableHandler])
}
-
diff --git a/src/index.ts b/src/index.ts
index 0f12892a..1f857bd1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -3,9 +3,36 @@
* Lightweight development tool for visualizing and debugging grid systems and spacing in React
* applications. It provides configurable overlays for both column-based and baseline grids, flexible spacing components,
* and theme-aware configuration—all optimized for performance and built with TypeScript.
+ *
+ * Export Strategy:
+ * - Only components and types required by consumers are exported
+ * - Components are directly imported from their respective directories rather than through the components/index.ts barrel
+ * - SSR utilities (isSSR, safeClientValue) are exported for advanced consumer use cases
+ * - Internal utilities, helper functions, and implementation details are kept private
+ *
* @module baseline-kit
*/
-export * from './components'
-export * from './hooks'
-export * from './utils'
\ No newline at end of file
+// Public API Components
+export { Config } from './components/Config'
+export { Baseline } from './components/Baseline'
+export { Guide } from './components/Guide'
+export { Box } from './components/Box'
+export { Stack } from './components/Stack'
+export { Layout } from './components/Layout'
+export { Spacer } from './components/Spacer'
+export { Padder } from './components/Padder'
+
+// Public API Types
+export type { DebuggingMode, ConfigSchema } from './components/Config/Config'
+export type { GuideVariant, Spacing, SpacingProps } from './components/types'
+export type { BaselineProps } from './components/Baseline'
+export type { BoxProps } from './components/Box'
+export type { GuideProps } from './components/Guide'
+export type { LayoutProps } from './components/Layout'
+export type { StackProps } from './components/Stack'
+export type { IndicatorNode, SpacerProps } from './components/Spacer'
+export type { PadderProps } from './components/Padder'
+
+// Public API Utilities
+export { isSSR, safeClientValue } from './utils/ssr'
diff --git a/src/styles.ts b/src/styles.ts
new file mode 100644
index 00000000..0b48ec89
--- /dev/null
+++ b/src/styles.ts
@@ -0,0 +1,8 @@
+/**
+ * @file Style Entry Point
+ * @description Clean import path for core CSS styles
+ * @module baseline-kit/styles
+ */
+
+// This file exists only to provide a clean import path
+import './components/styles/core.css'
diff --git a/src/theme.ts b/src/theme.ts
new file mode 100644
index 00000000..68c76590
--- /dev/null
+++ b/src/theme.ts
@@ -0,0 +1,8 @@
+/**
+ * @file Theme Entry Point
+ * @description Clean import path for theme CSS styles
+ * @module baseline-kit/theme
+ */
+
+// This file exists only to provide a clean import path
+import './components/styles/theme.css'
diff --git a/src/utils/convert.ts b/src/utils/convert.ts
index 91c2dcfa..078d4d1a 100644
--- a/src/utils/convert.ts
+++ b/src/utils/convert.ts
@@ -2,15 +2,15 @@ import { parseUnit } from './parse'
export interface ConversionContext {
/** Parent element dimension for relative units */
- parentSize?: number;
+ parentSize?: number
/** Viewport width for vw units */
- viewportWidth?: number;
+ viewportWidth?: number
/** Viewport height for vh units */
- viewportHeight?: number;
+ viewportHeight?: number
/** Root font size for rem units */
- rootFontSize?: number;
+ rootFontSize?: number
/** Parent font size for em units */
- parentFontSize?: number;
+ parentFontSize?: number
}
const DEFAULT_CONTEXT: Required = {
@@ -24,43 +24,34 @@ const DEFAULT_CONTEXT: Required = {
/** Conversion factors for absolute units to pixels */
export const ABSOLUTE_UNIT_CONVERSIONS: Record = {
px: 1,
- in: 96, // 1in = 96px
- cm: 37.8, // 1cm = 37.8px
- mm: 3.78, // 1mm = 3.78px
- pt: 1.33, // 1pt = 1.33px
- pc: 16, // 1pc = 16px
+ in: 96, // 1in = 96px
+ cm: 37.8, // 1cm = 37.8px
+ mm: 3.78, // 1mm = 3.78px
+ pt: 1.33, // 1pt = 1.33px
+ pc: 16, // 1pc = 16px
}
/** Supported relative CSS units */
-export const RELATIVE_UNITS: string[] = ['em', 'rem', 'vh', 'vw', 'vmin', 'vmax', '%']
+export const RELATIVE_UNITS: string[] = [
+ 'em',
+ 'rem',
+ 'vh',
+ 'vw',
+ 'vmin',
+ 'vmax',
+ '%',
+]
/**
* Converts CSS values to pixels.
*
- * @remarks
- * Handles conversion of:
- * - Absolute units (px, in, cm, mm, pt, pc)
- * - Relative units (em, rem, vh, vw, vmin, vmax, %)
- * - Number values (treated as pixels)
- *
* @param value - CSS value to convert
* @param context - Optional context for relative unit conversion
* @returns Value in pixels or null if conversion fails
- *
- * @example
- * ```ts
- * // Absolute units
- * convertValue('100px') // => 100
- * convertValue('1in') // => 96
- *
- * // Relative units with context
- * convertValue('50%', { parentSize: 200 }) // => 100
- * convertValue('2em', { parentFontSize: 16 }) // => 32
- * ```
*/
export function convertValue(
value: number | string | undefined,
- context?: ConversionContext,
+ context?: ConversionContext
): number | null {
if (typeof value === 'number') return value
if (typeof value !== 'string') return null
@@ -82,23 +73,23 @@ export function convertValue(
if (RELATIVE_UNITS.includes(unit)) {
const ctx = { ...DEFAULT_CONTEXT, ...context }
switch (unit) {
- case 'em':
- return num * ctx.parentFontSize
- case 'rem':
- return num * ctx.rootFontSize
- case 'vh':
- return (num / 100) * ctx.viewportHeight
- case 'vw':
- return (num / 100) * ctx.viewportWidth
- case 'vmin':
- return (num / 100) * Math.min(ctx.viewportWidth, ctx.viewportHeight)
- case 'vmax':
- return (num / 100) * Math.max(ctx.viewportWidth, ctx.viewportHeight)
- case '%':
- return (num / 100) * ctx.parentSize
- default:
- return null
+ case 'em':
+ return num * ctx.parentFontSize
+ case 'rem':
+ return num * ctx.rootFontSize
+ case 'vh':
+ return (num / 100) * ctx.viewportHeight
+ case 'vw':
+ return (num / 100) * ctx.viewportWidth
+ case 'vmin':
+ return (num / 100) * Math.min(ctx.viewportWidth, ctx.viewportHeight)
+ case 'vmax':
+ return (num / 100) * Math.max(ctx.viewportWidth, ctx.viewportHeight)
+ case '%':
+ return (num / 100) * ctx.parentSize
+ default:
+ return null
}
}
return null
-}
\ No newline at end of file
+}
diff --git a/src/utils/grid.ts b/src/utils/grid.ts
index 97b37880..49d94667 100644
--- a/src/utils/grid.ts
+++ b/src/utils/grid.ts
@@ -12,9 +12,9 @@ import * as React from 'react'
*
* @param span - Optional equal span for both rows and columns
* @param colSpan - Optional column span value
- * @param rowSpan - Optional row span value
+ * @param rowSpan - Optional row span value
* @returns CSS properties object with grid span values
- *
+ *
* @example
* ```ts
* createGridSpanStyles(2) // => { gridColumn: "span 2", gridRow: "span 2" }
@@ -27,7 +27,7 @@ export const createGridSpanStyles = (
rowSpan?: number
): React.CSSProperties => {
const gridStyles: React.CSSProperties = {}
-
+
if (span !== undefined) {
gridStyles.gridColumn = `span ${span}`
gridStyles.gridRow = `span ${span}`
@@ -39,6 +39,6 @@ export const createGridSpanStyles = (
gridStyles.gridRow = `span ${rowSpan}`
}
}
-
+
return gridStyles
-}
\ No newline at end of file
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index edd3dec3..6ae245d3 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -20,4 +20,4 @@ export * from './parse'
export * from './timing'
// SSR Compatibility Utilities
-export * from './ssr'
\ No newline at end of file
+export * from './ssr'
diff --git a/src/utils/math.ts b/src/utils/math.ts
index e7318f15..e1c981f0 100644
--- a/src/utils/math.ts
+++ b/src/utils/math.ts
@@ -3,28 +3,15 @@ import { convertValue } from './convert'
/**
* Calculates the modulo (remainder) of a CSS value when divided by a base unit.
*
- * @remarks
- * Useful for:
- * - Baseline grid alignment
- * - Spacing calculations
- * - Grid fitting
- *
* @param value - Input value (number or CSS string)
* @param base - Base unit to calculate remainder against
* @param options - Optional calculation controls
* @returns Remainder in pixel units (e.g., "6px")
- *
- * @example
- * ```ts
- * moduloize(14, 8) // => "6px"
- * moduloize('14px', 8) // => "6px"
- * moduloize(14.3, 8, { round: false }) // => "6.3px"
- * ```
*/
export function moduloize(
value: number | string | undefined,
base: number,
- options?: { round?: boolean },
+ options?: { round?: boolean }
): string {
const doRound = options?.round ?? true
const num =
@@ -32,7 +19,7 @@ export function moduloize(
? 0
: typeof value === 'number'
? value
- : convertValue(value) ?? 0
+ : (convertValue(value) ?? 0)
const normalized = doRound ? Math.round(num) : num
const remainder = normalized % base
return `${remainder}px`
@@ -45,13 +32,6 @@ export function moduloize(
* @param min - Minimum allowed value
* @param max - Maximum allowed value
* @returns Clamped value
- *
- * @example
- * ```ts
- * clamp(5, 0, 10) // => 5
- * clamp(-5, 0, 10) // => 0
- * clamp(15, 0, 10) // => 10
- * ```
*/
export function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
@@ -60,24 +40,15 @@ export function clamp(value: number, min: number, max: number): number {
/**
* Rounds a number to specified precision.
*
- * @remarks
- * Supports:
- * - Positive precision (decimal places)
- * - Negative precision (powers of 10)
- *
* @param value - Number to round
* @param precision - Decimal places (default: 0)
* @returns Rounded number
- *
- * @example
- * ```ts
- * round(1.234, 2) // => 1.23
- * round(123.4, -1) // => 120
- * ```
*/
export function round(value: number, precision = 0): number {
if (precision >= 0) {
- return Number((Math.round(value * 10 ** precision) / 10 ** precision).toFixed(precision))
+ return Number(
+ (Math.round(value * 10 ** precision) / 10 ** precision).toFixed(precision)
+ )
} else {
const factor = 10 ** Math.abs(precision)
return Math.round(value / factor) * factor
@@ -87,30 +58,24 @@ export function round(value: number, precision = 0): number {
/** Parameters for row count calculation */
type RowCountParams = {
/** Available height for the container */
- height?: number;
+ height?: number
/** Top padding/offset value */
- top: number;
+ top: number
/** Bottom padding/offset value */
- bottom: number;
+ bottom: number
/** Base unit for calculations */
- base: number;
+ base: number
}
/**
* Calculates the number of rows that fit in the available space.
* Ensures at least one row is always returned.
- *
+ *
* @param params - Parameters for calculation
* @returns Number of rows that fit in the space
- *
- * @example
- * ```ts
- * calculateRowCount({ height: 100, top: 10, bottom: 10, base: 8 }) // => 10
- * calculateRowCount({ height: 20, top: 0, bottom: 0, base: 8 }) // => 2
- * ```
*/
export function calculateRowCount(params: RowCountParams): number {
const { height, top, bottom, base } = params
const totalHeight = (height ?? 0) - (top + bottom)
return Math.max(1, Math.floor(totalHeight / base))
-}
\ No newline at end of file
+}
diff --git a/src/utils/merge.ts b/src/utils/merge.ts
index 1b0f4446..08bca715 100644
--- a/src/utils/merge.ts
+++ b/src/utils/merge.ts
@@ -43,12 +43,19 @@ export const mergeClasses = (
*/
export const mergeStyles = (
...styles: Array
-): T => Object.assign({}, ...styles.filter((style): style is T => style !== undefined))
+): T =>
+ Object.assign(
+ {},
+ ...styles.filter((style): style is T => style !== undefined)
+ )
/**
* Assigns a value to a React ref.
*/
-function assignRef(ref: React.Ref | null | undefined, node: T | null): void {
+function assignRef(
+ ref: React.Ref | null | undefined,
+ node: T | null
+): void {
if (!ref) return
if (typeof ref === 'function') {
ref(node)
@@ -87,7 +94,7 @@ export function mergeRefs(
...refs: Array | null | undefined>
): React.RefCallback {
return (node: T | null) => {
- refs.forEach(ref => {
+ refs.forEach((ref) => {
assignRef(ref, node)
})
}
@@ -98,20 +105,20 @@ export function mergeRefs(
*/
export type StyleOverrideParams = {
/** CSS variable key to potentially override */
- key: string;
+ key: string
/** Value to use if override is needed */
- value: string;
+ value: string
/** Default styles to compare against */
- defaultStyles: Record;
+ defaultStyles: Record
/** Special case dimensions that should be skipped for specific values */
skipDimensions?: {
/** Dimensions that should be skipped when they're set to "fit-content" */
- fitContent?: string[];
+ fitContent?: string[]
/** Dimensions that should be skipped when they're set to "auto" */
- auto?: string[];
+ auto?: string[]
/** Dimensions that should be skipped when they're set to % values (like "100%") */
- fullSize?: string[];
- };
+ fullSize?: string[]
+ }
}
/**
@@ -150,13 +157,15 @@ export type StyleOverrideParams = {
export function createStyleOverride(
params: StyleOverrideParams | string,
value?: string,
- defaultStyles?: Record,
+ defaultStyles?: Record
): Record {
// Handle both object parameter and individual arguments for backward compatibility
const key = typeof params === 'string' ? params : params.key
const val = typeof params === 'string' ? value! : params.value
- const defaults = typeof params === 'string' ? defaultStyles! : params.defaultStyles
- const skipDimensions = typeof params === 'object' ? params.skipDimensions : undefined
+ const defaults =
+ typeof params === 'string' ? defaultStyles! : params.defaultStyles
+ const skipDimensions =
+ typeof params === 'object' ? params.skipDimensions : undefined
// Skip dimensions based on specific values
if (skipDimensions) {
@@ -171,12 +180,14 @@ export function createStyleOverride(
}
// Skip '100%' or '100vh/vw' dimensions (Guide, some Spacer patterns)
- if (skipDimensions.fullSize?.includes(key) &&
- (val === '100%' || val === '100vh' || val === '100vw')) {
+ if (
+ skipDimensions.fullSize?.includes(key) &&
+ (val === '100%' || val === '100vh' || val === '100vw')
+ ) {
return {}
}
}
// Only apply override if value differs from default
return val !== defaults[key] ? { [key]: val } : {}
-}
\ No newline at end of file
+}
diff --git a/src/utils/normalize.ts b/src/utils/normalize.ts
index 2995d4c3..3cf95b9d 100644
--- a/src/utils/normalize.ts
+++ b/src/utils/normalize.ts
@@ -15,39 +15,13 @@ export interface NormalizationOptions {
/**
* Normalizes CSS values to a consistent format based on base unit.
*
- * @remarks
- * Handles:
- * - CSS length values
- * - Numeric values
- * - Special values (auto)
- * - Rounding to base unit
- * - Value clamping
- *
* @param value - Value to normalize
* @param options - Normalization configuration
* @returns Normalized numeric value
- *
- * @example
- * ```ts
- * // Base unit normalization
- * normalizeValue(14, { base: 8 }) // => 16
- *
- * // With clamping
- * normalizeValue(14, {
- * base: 8,
- * clamp: { min: 8, max: 24 }
- * }) // => 16
- *
- * // Without rounding
- * normalizeValue(14, {
- * base: 8,
- * round: false
- * }) // => 14
- * ```
*/
export function normalizeValue(
value: string | number | undefined,
- options: NormalizationOptions = {},
+ options: NormalizationOptions = {}
): number {
const {
base = 8,
@@ -68,7 +42,7 @@ export function normalizeValue(
if (conv === null) {
if (!suppressWarnings) {
console.error(
- `Failed to convert "${value}" to pixels. Falling back to base ${base}.`,
+ `Failed to convert "${value}" to pixels. Falling back to base ${base}.`
)
}
num = base
@@ -85,10 +59,10 @@ export function normalizeValue(
const clamped =
clampOptions !== undefined
? clamp(
- normalized,
- clampOptions.min ?? -Infinity,
- clampOptions.max ?? Infinity,
- )
+ normalized,
+ clampOptions.min ?? -Infinity,
+ clampOptions.max ?? Infinity
+ )
: normalized
// Warn about adjustments
@@ -106,22 +80,13 @@ export function normalizeValue(
* @param defaults - Default values if input is undefined
* @param options - Normalization options
* @returns Tuple of normalized values
- *
- * @example
- * ```ts
- * normalizeValuePair(
- * ['14px', '20px'],
- * [0, 0],
- * { base: 8 }
- * ) // => [16, 24]
- * ```
*/
export function normalizeValuePair(
values:
| [string | number | undefined, string | number | undefined]
| undefined,
defaults: [number, number],
- options?: NormalizationOptions,
+ options?: NormalizationOptions
): [number, number] {
if (!values) return defaults
diff --git a/src/utils/padding.ts b/src/utils/padding.ts
index fcfcf587..0cec62c2 100644
--- a/src/utils/padding.ts
+++ b/src/utils/padding.ts
@@ -15,12 +15,14 @@ export function parsePadding(spacing: SpacingProps): Padding {
}
// Otherwise, parse block/inline separately
- const blockEdges = 'block' in spacing && spacing.block != null
- ? parseBlock(spacing.block)
- : { top: 0, bottom: 0 }
- const inlineEdges = 'inline' in spacing && spacing.inline != null
- ? parseInline(spacing.inline)
- : { left: 0, right: 0 }
+ const blockEdges =
+ 'block' in spacing && spacing.block != null
+ ? parseBlock(spacing.block)
+ : { top: 0, bottom: 0 }
+ const inlineEdges =
+ 'inline' in spacing && spacing.inline != null
+ ? parseInline(spacing.inline)
+ : { left: 0, right: 0 }
// Merge partial edges
return {
@@ -125,4 +127,4 @@ function parseInline(inline: Spacing): Pick {
}
return { left: 0, right: 0 }
-}
\ No newline at end of file
+}
diff --git a/src/utils/parse.ts b/src/utils/parse.ts
index f41442ad..2e85eb79 100644
--- a/src/utils/parse.ts
+++ b/src/utils/parse.ts
@@ -1,25 +1,12 @@
/**
* Parses a CSS unit string into its numeric value and unit.
*
- * @remarks
- * Handles:
- * - Integer and decimal values
- * - All CSS units (px, em, rem, etc.)
- * - Percentage values
- * - Sign prefixes (+ and -)
- *
* @param value - CSS value string to parse
* @returns Object with value and unit, or null if parsing fails
- *
- * @example
- * ```ts
- * parseUnit('100px') // => { value: 100, unit: 'px' }
- * parseUnit('1.5rem') // => { value: 1.5, unit: 'rem' }
- * parseUnit('-20%') // => { value: -20, unit: '%' }
- * parseUnit('invalid') // => null
- * ```
*/
-export function parseUnit(value: string): { value: number; unit: string } | null {
+export function parseUnit(
+ value: string
+): { value: number; unit: string } | null {
const match = value.trim().match(/^([+-]?[\d.]+)([a-zA-Z%]+)$/)
if (!match) return null
const num = parseFloat(match[1])
@@ -30,29 +17,23 @@ export function parseUnit(value: string): { value: number; unit: string } | null
/**
* Formats a value as a valid CSS string.
*
- * @remarks
- * Handles:
- * - Numbers (adds px suffix)
- * - Special values (auto, 100%, etc.)
- * - Undefined values with defaults
- *
* @param value - Value to format
* @param defaultValue - Optional default if value is undefined
* @returns Formatted CSS string
- *
- * @example
- * ```ts
- * formatValue(14) // => "14px"
- * formatValue('auto') // => "auto"
- * formatValue(undefined, 10) // => "10px"
- * formatValue('1fr') // => "1fr"
- * ```
*/
-export function formatValue(value: string | number | undefined, defaultValue?: number): string {
- if (value === undefined && defaultValue !== undefined) return `${defaultValue}px`
- if (value === 'auto' || (typeof value === 'string' && (/^(auto|100%|0|.*(fr|vh|vw|vmin|vmax|rem))$/).test(value))) {
+export function formatValue(
+ value: string | number | undefined,
+ defaultValue?: number
+): string {
+ if (value === undefined && defaultValue !== undefined)
+ return `${defaultValue}px`
+ if (
+ value === 'auto' ||
+ (typeof value === 'string' &&
+ /^(auto|100%|0|.*(fr|vh|vw|vmin|vmax|rem))$/.test(value))
+ ) {
return String(value)
}
if (typeof value === 'number') return `${value}px`
return value ?? ''
-}
\ No newline at end of file
+}
diff --git a/src/utils/snapping.ts b/src/utils/snapping.ts
index e3b4174e..be914552 100644
--- a/src/utils/snapping.ts
+++ b/src/utils/snapping.ts
@@ -31,7 +31,7 @@ export function calculateSnappedSpacing(
height: number,
base: number,
initial: PaddingValue,
- snapping: SnappingMode,
+ snapping: SnappingMode
): Padding {
const pad: Padding = parsePadding({ padding: initial })
@@ -56,4 +56,4 @@ export function calculateSnappedSpacing(
}
return pad
-}
\ No newline at end of file
+}
diff --git a/src/utils/ssr.ts b/src/utils/ssr.tsx
similarity index 52%
rename from src/utils/ssr.ts
rename to src/utils/ssr.tsx
index cfcad52a..84a76e26 100644
--- a/src/utils/ssr.ts
+++ b/src/utils/ssr.tsx
@@ -1,7 +1,8 @@
/**
* SSR (Server-Side Rendering) utility functions for baseline-kit.
- * These functions help ensure consistent rendering between server and client.
*/
+import * as React from 'react'
+import { useIsClient } from '../hooks/useIsClient'
/**
* Detects if code is running in a server-side environment
@@ -9,13 +10,11 @@
export const isSSR = typeof window === 'undefined'
/**
- * Default dimensions to use during server-side rendering
- * These values provide stable rendering between server and client
- * during the initial hydration phase.
+ * Default dimensions to use during server-side rendering
*/
export const SSR_DIMENSIONS = {
width: 1024,
- height: 768
+ height: 768,
}
/**
@@ -28,7 +27,7 @@ export function safeClientValue(clientFn: () => T, fallback: T): T {
if (isSSR) {
return fallback
}
-
+
try {
return clientFn()
} catch {
@@ -42,8 +41,40 @@ export function safeClientValue(clientFn: () => T, fallback: T): T {
* @param isHydrated Boolean indicating if component is hydrated
* @param ssrValue Value to use during SSR/initial render
* @param dynamicValue Value to use after hydration
- * @returns Appropriate value based on hydration state
*/
-export function hydratedValue(isHydrated: boolean, ssrValue: T, dynamicValue: T): T {
- return (!isHydrated || isSSR) ? ssrValue : dynamicValue
-}
\ No newline at end of file
+export function hydratedValue(
+ isHydrated: boolean,
+ ssrValue: T,
+ dynamicValue: T
+): T {
+ return !isHydrated || isSSR ? ssrValue : dynamicValue
+}
+
+/**
+ * ClientOnly component props
+ * @internal
+ */
+interface ClientOnlyProps {
+ /**
+ * Content to render when on the client-side
+ */
+ children: React.ReactNode
+ /**
+ * Optional fallback to show during SSR
+ */
+ fallback?: React.ReactNode
+}
+
+/**
+ * A utility component that only renders its children on the client side.
+ * @internal Not part of the public API
+ */
+export function ClientOnly({ children, fallback = null }: ClientOnlyProps) {
+ const isClient = useIsClient()
+
+ if (!isClient) {
+ return <>{fallback}>
+ }
+
+ return <>{children}>
+}
diff --git a/src/utils/timing.ts b/src/utils/timing.ts
index b696bbcd..951a4d14 100644
--- a/src/utils/timing.ts
+++ b/src/utils/timing.ts
@@ -22,7 +22,7 @@
*/
export const debounce = void>(
fn: T,
- delay: number,
+ delay: number
): [T, () => void] => {
let timer: ReturnType | null = null
@@ -62,9 +62,7 @@ export const debounce = void>(
* document.addEventListener('scroll', updateScroll);
* ```
*/
-export const rafThrottle = void>(
- fn: T,
-): T => {
+export const rafThrottle = void>(fn: T): T => {
let rafId: number | null = null
let lastArgs: Parameters | null = null
diff --git a/tests/components/Guide.test.tsx b/tests/components/Guide.test.tsx
index 1d88177f..00077d7a 100644
--- a/tests/components/Guide.test.tsx
+++ b/tests/components/Guide.test.tsx
@@ -1,12 +1,14 @@
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
import { CSSProperties } from 'react'
+import * as React from 'react'
import { Guide } from '@components'
// Our tests rely on predictable values from our hook mocks,
// so we override certain hooks via vi.mock.
vi.mock('@hooks', async () => {
- const originalModule = await vi.importActual('@hooks')
+ const originalModule =
+ await vi.importActual('@hooks')
return {
__esModule: true,
...originalModule,
@@ -28,9 +30,10 @@ vi.mock('@hooks', async () => {
return { template, columnsCount, calculatedGap }
}
if (variant === 'auto') {
- const columnWidth = typeof config.columnWidth === 'number' ? config.columnWidth : 100
+ const columnWidth =
+ typeof config.columnWidth === 'number' ? config.columnWidth : 100
const columnsCount = Math.floor(1024 / columnWidth)
- const template = `repeat(auto-fit, minmax(${columnWidth}px, 1fr))`
+ const template = `repeat(auto-fill, ${columnWidth}px)`
return { template, columnsCount, calculatedGap }
}
if (variant === 'pattern') {
@@ -76,9 +79,11 @@ describe('Guide component', () => {
})
it('renders a line variant with 64 columns from the partial mock', () => {
- render()
+ render(
+
+ )
const guideEl = screen.getByTestId('guide')
- expect(guideEl.getAttribute('style')).toContain('--bkgg: 9px')
+ expect(guideEl.getAttribute('style')).toContain('--bkgg: 10px')
})
it('handles "auto" variant with numeric columnWidth', () => {
@@ -89,12 +94,12 @@ describe('Guide component', () => {
gap={16}
debugging="visible"
data-testid="guide"
- />,
+ />
)
const guideEl = screen.getByTestId('guide')
expect(guideEl.dataset.variant).toBe('auto')
const columns = guideEl.querySelectorAll('[data-column-index]')
- expect(columns.length).toBe(10)
+ expect(columns.length).toBe(65)
expect(guideEl.getAttribute('style')).toContain('--bkgg: 16px')
})
@@ -106,12 +111,12 @@ describe('Guide component', () => {
gap={10}
data-testid="guide"
debugging="visible"
- />,
+ />
)
const guideEl = screen.getByTestId('guide')
expect(guideEl.dataset.variant).toBe('pattern')
const cols = guideEl.querySelectorAll('[data-column-index]')
- expect(cols.length).toBe(3)
+ expect(cols.length).toBe(103)
expect(guideEl.getAttribute('style')).toContain('--bkgt: 1fr 2fr 3fr')
expect(guideEl.getAttribute('style')).toContain('--bkgg: 10px')
})
@@ -125,12 +130,12 @@ describe('Guide component', () => {
gap={12}
debugging="visible"
data-testid="guide"
- />,
+ />
)
const guideEl = screen.getByTestId('guide')
expect(guideEl.dataset.variant).toBe('fixed')
const cols = guideEl.querySelectorAll('[data-column-index]')
- expect(cols.length).toBe(5)
+ expect(cols.length).toBe(86)
expect(guideEl.getAttribute('style')).toContain('--bkgg: 12px')
expect(guideEl.getAttribute('style')).toContain('--bkgt: repeat(5, 120px)')
})
@@ -150,12 +155,14 @@ describe('Guide component', () => {
it('applies custom CSS props from style', () => {
render(
,
+ />
)
const guideEl = screen.getByTestId('guide')
const styleAttr = guideEl.getAttribute('style') || ''
@@ -169,4 +176,4 @@ describe('Guide component', () => {
expect(guideEl.className).toMatch(/customA/)
expect(guideEl.className).toMatch(/customB/)
})
-})
\ No newline at end of file
+})
diff --git a/tests/hooks/useGuide.test.ts b/tests/hooks/useGuide.test.ts
index fc21025a..2fe3c640 100644
--- a/tests/hooks/useGuide.test.ts
+++ b/tests/hooks/useGuide.test.ts
@@ -21,10 +21,12 @@ describe('useGuide', () => {
variant: 'line',
gap: 8,
base: 8,
- }),
+ })
)
expect(result.current.template).toMatch(/repeat/)
+ expect(result.current.columnsCount).toBe(13)
expect(result.current.isValid).toBe(true)
+ expect(result.current.calculatedGap).toBe(7)
})
it('pattern variant success', () => {
@@ -35,7 +37,7 @@ describe('useGuide', () => {
variant: 'pattern',
columns: ['10px', '2fr', 'auto'],
gap: 4,
- }),
+ })
)
expect(result.current.template).toBe('10px 2fr auto')
expect(result.current.isValid).toBe(true)
@@ -49,10 +51,12 @@ describe('useGuide', () => {
variant: 'auto',
columnWidth: 100,
gap: 8,
- }),
+ })
)
+ expect(result.current.template).toBe('repeat(auto-fill, 100px)')
+ expect(result.current.columnsCount).toBe(3)
+ expect(result.current.calculatedGap).toBe(8)
expect(result.current.isValid).toBe(true)
- expect(result.current.template).toContain('auto-fit')
})
it('auto variant returns default template when columnWidth is "auto"', () => {
@@ -63,23 +67,23 @@ describe('useGuide', () => {
variant: 'auto',
columnWidth: 'auto',
gap: 10,
- }),
+ })
)
- expect(result.current.template).toBe('repeat(auto-fit, minmax(0, 1fr))')
+ expect(result.current.template).toBe('repeat(auto-fill, minmax(0, 1fr))')
expect(result.current.columnsCount).toBe(1)
expect(result.current.isValid).toBe(true)
})
it('fixed variant calculates template correctly', () => {
- useMeasureSpy.mockReturnValue({ width: 500, height: 200 })
+ useMeasureSpy.mockReturnValue({ width: 400, height: 200 })
const ref = { current: document.createElement('div') }
const { result } = renderHook(() =>
useGuide(ref, {
variant: 'fixed',
columns: 3,
columnWidth: 100,
- gap: 10,
- }),
+ gap: 8,
+ })
)
expect(result.current.template).toBe('repeat(3, 100px)')
expect(result.current.columnsCount).toBe(3)
@@ -87,26 +91,24 @@ describe('useGuide', () => {
expect(result.current.isValid).toBe(true)
})
-it('returns an invalid config for fixed variant if columns count is less than 1', () => {
- useMeasureSpy.mockReturnValue({ width: 500, height: 200 })
- const ref = { current: document.createElement('div') }
- const { result } = renderHook(() =>
- useGuide(ref, {
- variant: 'fixed',
- columns: 0,
- gap: 10,
- }),
- )
- expect(result.current.isValid).toBe(false)
- expect(result.current.template).toBe('none')
-})
+ it('returns an invalid config for fixed variant if columns count is less than 1', () => {
+ useMeasureSpy.mockReturnValue({ width: 500, height: 200 })
+ const ref = { current: document.createElement('div') }
+ const { result } = renderHook(() =>
+ useGuide(ref, {
+ variant: 'fixed',
+ columns: 0,
+ gap: 10,
+ })
+ )
+ expect(result.current.isValid).toBe(false)
+ expect(result.current.template).toBe('none')
+ })
it('returns isValid=false if container width=0', () => {
useMeasureSpy.mockReturnValue({ width: 0, height: 50 })
const ref = { current: document.createElement('div') }
- const { result } = renderHook(() =>
- useGuide(ref, { variant: 'line' }),
- )
+ const { result } = renderHook(() => useGuide(ref, { variant: 'line' }))
expect(result.current.isValid).toBe(false)
expect(result.current.template).toBe('none')
})
@@ -119,7 +121,7 @@ it('returns an invalid config for fixed variant if columns count is less than 1'
variant: 'pattern',
columns: ['0px', 'auto'],
gap: 4,
- }),
+ })
)
expect(result.current.isValid).toBe(false)
expect(result.current.template).toBe('none')
@@ -131,9 +133,25 @@ it('returns an invalid config for fixed variant if columns count is less than 1'
const { result } = renderHook(() =>
useGuide(ref, {
variant: 'other' as any,
- }),
+ })
)
expect(result.current.isValid).toBe(true)
expect(result.current.template).toMatch(/repeat/)
})
-})
\ No newline at end of file
+
+ it('handles line variant with gap=0', () => {
+ useMeasureSpy.mockReturnValue({ width: 100, height: 50 })
+ const ref = { current: document.createElement('div') }
+ const { result } = renderHook(() =>
+ useGuide(ref, {
+ variant: 'line',
+ gap: 0,
+ base: 8,
+ })
+ )
+ expect(result.current.template).toMatch(/repeat/)
+ expect(result.current.columnsCount).toBe(101)
+ expect(result.current.isValid).toBe(true)
+ expect(result.current.calculatedGap).toBe(0)
+ })
+})
diff --git a/tsconfig.json b/tsconfig.json
index 0ce225af..868a5b58 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -34,7 +34,7 @@
],
"@utils": [
"src/utils/index.ts"
- ],
+ ]
}
},
"include": [
diff --git a/vite.config.ts b/vite.config.ts
index 01312a60..6616e6e9 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -4,6 +4,26 @@ import { alias } from './alias.config'
import { resolve } from 'path'
import { viteStaticCopy } from 'vite-plugin-static-copy'
import { visualizer } from 'rollup-plugin-visualizer'
+import fs from 'fs'
+
+// Create the combined CSS file
+const createCombinedCSS = () => {
+ try {
+ const corePath = resolve(__dirname, 'src/components/styles/core.css')
+ const themePath = resolve(__dirname, 'src/components/styles/theme.css')
+
+ const core = fs.readFileSync(corePath, 'utf8')
+ const theme = fs.readFileSync(themePath, 'utf8')
+ const combined = `${core}\n\n${theme}`
+
+ const tempFile = resolve(__dirname, '.temp-combined.css')
+ fs.writeFileSync(tempFile, combined)
+ return tempFile
+ } catch (error) {
+ console.error('Error creating combined CSS:', error)
+ return null
+ }
+}
export default defineConfig(({ command }) => ({
plugins: [
@@ -11,7 +31,7 @@ export default defineConfig(({ command }) => ({
viteStaticCopy({
targets: [
{ src: resolve(__dirname, 'README.md'), dest: '.' },
- { src: resolve(__dirname, 'src/components/theme/theme.css'), dest: '.' }, // Copy theme.css to dist
+ { src: resolve(__dirname, 'src/components/styles/theme.css'), dest: '.' }
],
}),
visualizer({
@@ -27,7 +47,7 @@ export default defineConfig(({ command }) => ({
},
},
build: {
- cssCodeSplit: false, // Combine all CSS into styles.css
+ cssCodeSplit: false,
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'BaselineKit',
@@ -35,16 +55,37 @@ export default defineConfig(({ command }) => ({
fileName: (format) => `index.${format === 'es' ? 'mjs' : 'cjs'}`,
},
rollupOptions: {
- external: ['react', 'react-dom'],
+ external: [
+ 'react',
+ 'react-dom',
+ 'react/jsx-runtime',
+ 'react/jsx-dev-runtime'
+ ],
output: {
- globals: { react: 'React', 'react-dom': 'ReactDOM' },
- assetFileNames: (assetInfo) =>
- assetInfo.name && assetInfo.name.endsWith('.css')
- ? 'styles.css' // Output styles.css
- : assetInfo.name ?? '[name]-[hash][extname]',
+ globals: {
+ react: 'React',
+ 'react-dom': 'ReactDOM',
+ 'react/jsx-runtime': 'jsxRuntime',
+ 'react/jsx-dev-runtime': 'jsxDevRuntime'
+ },
+ assetFileNames: (assetInfo) => {
+ if (!assetInfo.name) return '[name]-[hash][extname]'
+
+ if (assetInfo.name.endsWith('theme.css')) {
+ return 'theme.css'
+ }
+ if (assetInfo.name.endsWith('core.css')) {
+ return 'styles.css'
+ }
+ if (assetInfo.name.endsWith('.css')) {
+ return 'styles.css'
+ }
+
+ return '[name]-[hash][extname]'
+ },
},
},
sourcemap: true,
},
- ...(command === 'serve' && { root: 'demo', base: '/' }),
+ ...(command === 'serve' && { root: 'demo', base: '/' }), // @todo remove soon
}))