Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ea386c4
Create SECURITY.md
davedbase May 20, 2026
9d5eb64
Initial implementation of Storybook
davedbase May 25, 2026
8ead43a
Patch pathing
davedbase May 25, 2026
02be3e7
Added documentation pages
davedbase May 25, 2026
4b5c2a4
Separated stories
davedbase May 25, 2026
e2bae96
Patched types and lint details
davedbase May 25, 2026
fc77797
Added docuemntation details on how to create new stories
davedbase May 25, 2026
8ac86cf
Merge branch 'next' into v2/add-storybook
davedbase May 25, 2026
f3735df
Added additional stories and cleaned them up
davedbase May 25, 2026
feb60f7
Merge branch 'main' into v2/add-storybook
davedbase May 25, 2026
f4b3a13
Added examples for autofocus and bounds
davedbase May 25, 2026
ab19abc
ADded masonry, geolocation and clipboard
davedbase May 25, 2026
57f92fb
Remove demo links in the readme
davedbase May 25, 2026
4cd4a6d
Merge branch 'v2/add-storybook' of https://github.com/davedbase/solid…
davedbase May 25, 2026
0b5b7cc
Added input-mask, memo and keyboard examples
davedbase May 25, 2026
ecb6946
Fixed tabindex
davedbase May 25, 2026
50467ad
Added event-bus, interaction-observer and marker
davedbase May 25, 2026
8e2dea9
Added list-state, scroll, tween and keyed
davedbase May 25, 2026
c168c6e
Added mutation-observer, styles and virtual
davedbase May 26, 2026
36d3c23
Added presence, raf, trigger and orientation
davedbase May 26, 2026
33a7b87
Added event-listener, platform, scheduled and pointer
davedbase May 26, 2026
aafc336
Added broadcast-channel, controlled-signal and static-store
davedbase May 26, 2026
781e665
Consolidated and centralized UI components
davedbase May 26, 2026
50f9d34
Replaced button with components
davedbase May 26, 2026
c1643f1
Centralized Container and removed _helpers files
davedbase May 26, 2026
a10af42
Customized theme with colours, logo and font
davedbase May 26, 2026
91dfd4a
Patched Geist font replacement
davedbase May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,4 @@ _temp_*

# Local Netlify folder
.netlify
storybook-static
59 changes: 59 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { StorybookConfig } from "storybook-solidjs-vite";
import { mergeConfig } from "vite";

const config: StorybookConfig = {
stories: ["../packages/*/stories/*.stories.{ts,tsx}"],
staticDirs: [
{ from: "../packages/audio/stories/assets", to: "/audio" },
{ from: "../assets/img", to: "/img" },
{ from: "../node_modules/geist/dist/fonts", to: "/geist-fonts" },
],
addons: ["@storybook/addon-docs"],
framework: {
name: "storybook-solidjs-vite",
options: {},
},
docs: {},
// Swap Storybook's Nunito Sans woff2 files for Geist equivalents so that
// every existing `font-family: 'Nunito Sans'` rule in the manager runtime
// renders Geist without needing CSS overrides.
managerHead: (head = "") =>
head
.replace("./sb-common-assets/nunito-sans-regular.woff2", "/geist-fonts/geist-sans/Geist-Regular.woff2")
.replace("./sb-common-assets/nunito-sans-bold.woff2", "/geist-fonts/geist-sans/Geist-Bold.woff2")
.replace("./sb-common-assets/nunito-sans-italic.woff2", "/geist-fonts/geist-sans/Geist-Italic.woff2")
.replace(
"./sb-common-assets/nunito-sans-bold-italic.woff2",
"/geist-fonts/geist-sans/Geist-BoldItalic.woff2",
),
async viteFinal(config) {
return mergeConfig(config, {
plugins: [
// babel-preset-solid <beta.14 generates `addEventListener` but @solidjs/web@beta.14
// renamed it to `addEvent`. Patch any compiled story that uses the old import.
{
name: "solid-addEventListener-compat",
transform(code: string, id: string) {
if (/\.(tsx?|jsx?)$/.test(id) && code.includes("addEventListener as _$addEventListener")) {
return {
code: code.replace(
/\{ addEventListener as _\$addEventListener \}/g,
"{ addEvent as _$addEventListener }",
),
map: null,
};
}
},
},
],
resolve: {
conditions: ["@solid-primitives/source"],
// solid-js 2.0 removed the ./web subpath — redirect any lingering imports
// (e.g. from storybook-solidjs-vite's Solid 1.x code path) to @solidjs/web.
alias: [{ find: "solid-js/web", replacement: "@solidjs/web" }],
},
});
},
};

export default config;
23 changes: 23 additions & 0 deletions .storybook/manager-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<style>
@font-face {
font-family: "Geist";
src: url("/geist-fonts/geist-sans/Geist-Variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Geist";
src: url("/geist-fonts/geist-sans/Geist-Italic[wght].woff2") format("woff2");
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Geist Mono";
src: url("/geist-fonts/geist-mono/GeistMono-Variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
</style>
6 changes: 6 additions & 0 deletions .storybook/manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { addons } from "storybook/manager-api";
import { solidTheme } from "./theme";

addons.setConfig({
theme: solidTheme,
});
26 changes: 26 additions & 0 deletions .storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<style>
@font-face {
font-family: "Geist";
src: url("/geist-fonts/geist-sans/Geist-Variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Geist";
src: url("/geist-fonts/geist-sans/Geist-Italic[wght].woff2") format("woff2");
font-weight: 100 900;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Geist Mono";
src: url("/geist-fonts/geist-mono/GeistMono-Variable.woff2") format("woff2");
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
body {
font-family: "Geist", system-ui, sans-serif !important;
}
</style>
12 changes: 12 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { definePreview } from "storybook-solidjs-vite/next";
import * as docsAnnotations from "@storybook/addon-docs/preview";

export default definePreview({
addons: [docsAnnotations],
parameters: {
layout: "centered",
docs: {
toc: true,
},
},
});
40 changes: 40 additions & 0 deletions .storybook/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { create } from "storybook/theming/create";

export const solidTheme = create({
base: "dark",

brandTitle: "Solid Primitives",
brandImage: "/img/logo.png",
brandTarget: "_self",

// Solid.js brand blues (sourced from logo gradient stops)
colorPrimary: "#518ac8",
colorSecondary: "#76b3e1",

// App chrome
appBg: "#0d1b2a",
appContentBg: "#122033",
appPreviewBg: "#122033",
appBorderColor: "#1f3b77",
appBorderRadius: 6,

// Text
textColor: "#dcf2fd",
textInverseColor: "#0d1b2a",
textMutedColor: "#76b3e1",

// Toolbar / sidebar
barTextColor: "#76b3e1",
barHoverColor: "#dcf2fd",
barSelectedColor: "#76b3e1",
barBg: "#0d1b2a",

// Inputs
inputBg: "#1a2e45",
inputBorder: "#315aa9",
inputTextColor: "#dcf2fd",
inputBorderRadius: 4,

fontBase: '"Geist", "Inter", system-ui, sans-serif',
fontCode: '"Geist Mono", "Fira Code", monospace',
});
11 changes: 11 additions & 0 deletions .storybook/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"verbatimModuleSyntax": false,
"noUncheckedIndexedAccess": false,
"types": ["vite/client"]
},
"include": ["./**/*", "../packages/*/stories/**/*", "../template/stories/**/*"]
}
210 changes: 210 additions & 0 deletions .storybook/ui/controls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import type { JSX } from "solid-js";
import { colors, font, radii } from "./tokens.js";

export const Button = (props: {
onClick?: () => void;
children: JSX.Element;
variant?: "primary" | "secondary" | "outline" | "ghost";
color?: string;
disabled?: boolean;
style?: JSX.CSSProperties;
type?: "button" | "submit" | "reset";
ref?: HTMLButtonElement | ((el: HTMLButtonElement) => void);
}) => (
<button
ref={props.ref}
type={props.type ?? "button"}
onClick={props.onClick}
disabled={props.disabled}
style={{
padding: "0.5rem 1.1rem",
"border-radius": radii.md,
"font-size": font.sizeBase,
"font-weight": "500",
"font-family": font.system,
cursor: props.disabled ? "not-allowed" : "pointer",
opacity: props.disabled ? "0.5" : "1",
background:
props.color ??
(props.variant === "secondary"
? colors.secondary
: props.variant === "outline" || props.variant === "ghost"
? "transparent"
: colors.primary),
color:
props.color != null
? "white"
: props.variant === "secondary"
? colors.secondaryFg
: props.variant === "outline"
? "#1e293b"
: props.variant === "ghost"
? colors.muted
: colors.primaryFg,
border:
props.variant === "outline"
? `1px solid ${colors.border}`
: "none",
...(props.style ?? {}),
}}
>
{props.children}
</button>
);

export const btnStyle = {
padding: "0.4rem 0.85rem",
"border-radius": radii.md,
border: `1px solid ${colors.border}`,
background: colors.surface,
cursor: "pointer",
"font-family": font.system,
"font-size": font.sizeBase,
} as const;

export const inputStyle = {
padding: "0.4rem 0.75rem",
"font-size": font.sizeBase,
width: "100%",
border: `1px solid ${colors.border}`,
"border-radius": radii.md,
"box-sizing": "border-box",
"font-family": font.system,
} as const;

export const popoverStyle = {
background: "white",
border: `1px solid ${colors.border}`,
"border-radius": radii.lg,
padding: "1rem 1.25rem",
"box-shadow": "0 4px 16px rgba(0,0,0,0.10)",
"font-size": font.sizeBase,
color: "#475569",
} as const;

export const logBox = {
background: colors.surface,
"border-radius": radii.md,
border: `1px solid ${colors.border}`,
padding: "0.6rem 0.75rem",
"min-height": "80px",
"font-size": "0.85rem",
"line-height": "1.6",
} as const;

export const Label = (props: { children: string }) => (
<label style={{ "font-size": font.sizeSm, color: colors.muted }}>{props.children}</label>
);

export const TextField = (props: {
label?: string;
value: string;
onChange: (v: string) => void;
placeholder?: string;
type?: string;
}) => (
<div style={{ display: "flex", "flex-direction": "column", gap: "0.3rem" }}>
{props.label && <Label>{props.label}</Label>}
<input
type={props.type ?? "text"}
value={props.value}
onInput={e => props.onChange(e.currentTarget.value)}
placeholder={props.placeholder}
style={inputStyle}
/>
</div>
);

export const Kbd = (props: { children: JSX.Element }) => (
<kbd
style={{
display: "inline-block",
padding: "0.15rem 0.45rem",
"font-family": font.mono,
"font-size": font.sizeSm,
background: colors.secondary,
border: `1px solid ${colors.borderStrong}`,
"border-bottom-width": "2px",
"border-radius": radii.sm,
"white-space": "nowrap",
}}
>
{props.children}
</kbd>
);

export const Badge = (props: {
children: JSX.Element;
variant?: "default" | "info" | "success" | "warning" | "error";
}) => (
<span
style={{
display: "inline-block",
padding: "0.1rem 0.5rem",
"border-radius": radii.full,
"font-size": font.sizeSm,
"font-weight": "500",
background:
props.variant === "success"
? "#dcfce7"
: props.variant === "warning"
? "#fef9c3"
: props.variant === "error"
? "#fee2e2"
: props.variant === "info"
? "#dbeafe"
: colors.secondary,
color:
props.variant === "success"
? "#16a34a"
: props.variant === "warning"
? "#ca8a04"
: props.variant === "error"
? "#dc2626"
: props.variant === "info"
? "#2563eb"
: colors.secondaryFg,
}}
>
{props.children}
</span>
);

export const Code = (props: { children: JSX.Element }) => (
<code
style={{
"font-size": font.sizeSm,
background: colors.secondary,
padding: "0.1rem 0.4rem",
"border-radius": radii.sm,
"font-family": font.mono,
}}
>
{props.children}
</code>
);

export const Alert = (props: {
children: JSX.Element;
variant?: "info" | "warning" | "error";
}) => (
<div
style={{
background:
props.variant === "error" ? "#fee2e2" : props.variant === "info" ? "#dbeafe" : "#fef9c3",
color:
props.variant === "error" ? "#dc2626" : props.variant === "info" ? "#1d4ed8" : "#92400e",
border:
props.variant === "error"
? "1px solid #fca5a5"
: props.variant === "info"
? "1px solid #93c5fd"
: "1px solid #fde68a",
"border-radius": radii.md,
padding: "0.65rem 0.9rem",
"font-size": font.sizeBase,
}}
>
{props.children}
</div>
);
Loading