From 966a0ce4b0e6d241aad5f5aa8fa407cb636123a0 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 26 Dec 2025 21:41:36 -0800 Subject: [PATCH] gucci --- README.md | 45 +++--- examples/button.tsx | 115 ++++++-------- examples/helpers.tsx | 43 ++---- examples/navigation.tsx | 215 +++++++++++--------------- examples/profile.tsx | 178 +++++++++------------- src/index.tsx | 61 +++++--- src/tests/index.test.tsx | 315 +++++++++++++++++++++++++-------------- src/types.ts | 129 +++++++++++----- 8 files changed, 571 insertions(+), 530 deletions(-) diff --git a/README.md b/README.md index 171e0a5..1301f7a 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,15 @@ Example: import { define } from "forge" export const Button = define("button", { - layout: { - padding: 20, - }, - look: { - background: "blue", - }, + base: "button", + + padding: 20, + background: "blue", + variants: { kind: { - danger: { look: { background: "red" } }, - warning: { look: { background: "yellow" } }, + danger: { background: "red" }, + warning: { background: "yellow" }, } }, }) @@ -26,35 +25,31 @@ export const Button = define("button", { export const Profile = define("div", { - layout: { - padding: 50, - }, - look: { - background: "red", - }, + padding: 50, + background: "red", parts: { - header: { layout: { display: "flex" } }, - avatar: { layout: { width: 50 } }, - bio: { look: { color: "gray" } }, + Header: { display: "flex" }, + Avatar: { base: 'img', width: 50 }, + Bio: { color: "gray" }, }, variants: { size: { small: { - parts: { avatar: { layout: { width: 20 }}} + parts: { Avatar: { width: 20 }} } } }, - render({ props, parts: { root, header, avatar, bio } }) { + render({ props, parts: { Root, Header, Avatar, Bio } }) { return ( - -
- - {props.bio} -
-
+ +
+ + {props.bio} +
+
) }, }) diff --git a/examples/button.tsx b/examples/button.tsx index 12e2b3b..f51e4ef 100644 --- a/examples/button.tsx +++ b/examples/button.tsx @@ -4,109 +4,80 @@ import { Layout, ExampleSection } from './helpers' const Button = define('Button', { base: 'button', - layout: { - padding: "12px 24px", - display: "inline-flex", - alignItems: "center", - justifyContent: "center", - gap: 8, - }, - - look: { - background: "#3b82f6", - color: "white", - border: "none", - borderRadius: 8, - fontSize: 16, - fontWeight: 600, - cursor: "pointer", - transition: "all 0.2s ease", - userSelect: "none", - boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", - transform: "translateY(0)", - }, + padding: "12px 24px", + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + gap: 8, + background: "#3b82f6", + color: "white", + border: "none", + borderRadius: 8, + fontSize: 16, + fontWeight: 600, + cursor: "pointer", + transition: "all 0.2s ease", + userSelect: "none", + boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", + transform: "translateY(0)", states: { ":not(:disabled):hover": { - look: { - transform: 'translateY(-2px) !important', - filter: 'brightness(1.05)' - } + transform: 'translateY(-2px) !important', + filter: 'brightness(1.05)' }, ":not(:disabled):active": { - look: { - transform: 'translateY(1px) !important', - boxShadow: '0 2px 3px rgba(0, 0, 0, 0.2) !important' - } + transform: 'translateY(1px) !important', + boxShadow: '0 2px 3px rgba(0, 0, 0, 0.2) !important' }, }, variants: { intent: { primary: { - look: { - background: "#3b82f6", - color: "white", - boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", - }, + background: "#3b82f6", + color: "white", + boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", }, secondary: { - look: { - background: "#f3f4f6", - color: "#374151", - boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)", - }, + background: "#f3f4f6", + color: "#374151", + boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)", }, danger: { - look: { - background: "#ef4444", - color: "white", - boxShadow: "0 4px 6px rgba(239, 68, 68, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", - }, + background: "#ef4444", + color: "white", + boxShadow: "0 4px 6px rgba(239, 68, 68, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", }, ghost: { - look: { - background: "transparent", - color: "#aaa", - boxShadow: "0 4px 6px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1)", - border: "1px solid #eee", - }, + background: "transparent", + color: "#aaa", + boxShadow: "0 4px 6px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1)", + border: "1px solid #eee", }, }, size: { small: { - layout: { - padding: "8px 16px", - }, - look: { - fontSize: 14, - }, + padding: "8px 16px", + fontSize: 14, }, large: { - layout: { - padding: "16px 32px", - }, - look: { - fontSize: 18, - }, + padding: "16px 32px", + fontSize: 18, }, }, disabled: { - look: { - opacity: 0.5, - cursor: "not-allowed", - }, + opacity: 0.5, + cursor: "not-allowed", }, }, }) const ButtonRow = define('ButtonRow', { - layout: { - display: 'flex', - gap: 16, - flexWrap: 'wrap', - alignItems: 'center', - } + display: 'flex', + gap: 16, + flexWrap: 'wrap', + alignItems: 'center', }) export const ButtonExamplesPage = () => ( diff --git a/examples/helpers.tsx b/examples/helpers.tsx index ea46141..b294d82 100644 --- a/examples/helpers.tsx +++ b/examples/helpers.tsx @@ -2,48 +2,35 @@ import { define, Styles } from '../src' export const Body = define('Body', { base: 'body', - layout: { - margin: 0, - padding: '40px 20px', - }, - look: { - fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", - background: '#f3f4f6', - } + margin: 0, + padding: '40px 20px', + fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", + background: '#f3f4f6', }) const Container = define('Container', { - layout: { - maxWidth: 1200, - margin: '0 auto' - } + maxWidth: 1200, + margin: '0 auto' }) export const Header = define('Header', { base: 'h1', - layout: { - marginBottom: 40, - }, - look: { - color: '#111827' - } + + marginBottom: 40, + color: '#111827' }) export const ExampleSection = define('ExampleSection', { - layout: { - marginBottom: 40 - }, + marginBottom: 40, + parts: { Header: { base: 'h2', - layout: { - marginBottom: 16 - }, - look: { - color: '#374151', - fontSize: 18 - } + + marginBottom: 16, + color: '#374151', + fontSize: 18 } }, render({ props, parts: { Root, Header } }) { diff --git a/examples/navigation.tsx b/examples/navigation.tsx index 571285b..1d46b4a 100644 --- a/examples/navigation.tsx +++ b/examples/navigation.tsx @@ -2,37 +2,29 @@ import { define } from '../src' import { Layout, ExampleSection } from './helpers' const Tabs = define('Tabs', { - layout: { - display: 'flex', - gap: 0, - }, - look: { - borderBottom: '2px solid #e5e7eb', - }, + display: 'flex', + gap: 0, + borderBottom: '2px solid #e5e7eb', parts: { Tab: { base: 'button', - layout: { - padding: '12px 24px', - position: 'relative', - marginBottom: -2, - }, - look: { - background: 'transparent', - border: 'none', - borderBottom: '2px solid transparent', - color: '#6b7280', - fontSize: 14, - fontWeight: 500, - cursor: 'pointer', - transition: 'all 0.2s ease', - }, + + padding: '12px 24px', + position: 'relative', + marginBottom: -2, + background: 'transparent', + border: 'none', + borderBottom: '2px solid transparent', + color: '#6b7280', + fontSize: 14, + fontWeight: 500, + cursor: 'pointer', + transition: 'all 0.2s ease', + states: { ':hover': { - look: { - color: '#111827', - } + color: '#111827', } } } @@ -42,10 +34,8 @@ const Tabs = define('Tabs', { active: { parts: { Tab: { - look: { - color: '#3b82f6', - borderBottom: '2px solid #3b82f6', - }, + color: '#3b82f6', + borderBottom: '2px solid #3b82f6', } } } @@ -69,34 +59,28 @@ const Tabs = define('Tabs', { }) const Pills = define('Pills', { - layout: { - display: 'flex', - gap: 8, - flexWrap: 'wrap', - }, + display: 'flex', + gap: 8, + flexWrap: 'wrap', parts: { Pill: { base: 'button', - layout: { - padding: '8px 16px', - }, - look: { - background: '#f3f4f6', - border: 'none', - borderRadius: 20, - color: '#6b7280', - fontSize: 14, - fontWeight: 500, - cursor: 'pointer', - transition: 'all 0.2s ease', - }, + + padding: '8px 16px', + background: '#f3f4f6', + border: 'none', + borderRadius: 20, + color: '#6b7280', + fontSize: 14, + fontWeight: 500, + cursor: 'pointer', + transition: 'all 0.2s ease', + states: { ':hover': { - look: { - background: '#e5e7eb', - color: '#111827', - } + background: '#e5e7eb', + color: '#111827', } } } @@ -106,16 +90,12 @@ const Pills = define('Pills', { active: { parts: { Pill: { - look: { - background: '#3b82f6', - color: 'white', - }, + background: '#3b82f6', + color: 'white', states: { ':hover': { - look: { - background: '#2563eb', - color: 'white', - } + background: '#2563eb', + color: 'white', } } } @@ -141,52 +121,43 @@ const Pills = define('Pills', { }) const VerticalNav = define('VerticalNav', { - layout: { - display: 'flex', - flexDirection: 'column', - gap: 4, - width: 240, - }, + display: 'flex', + flexDirection: 'column', + gap: 4, + width: 240, parts: { NavItem: { base: 'a', - layout: { - padding: '12px 16px', - display: 'flex', - alignItems: 'center', - gap: 12, - }, - look: { - background: 'transparent', - borderRadius: 8, - color: '#6b7280', - fontSize: 14, - fontWeight: 500, - textDecoration: 'none', - cursor: 'pointer', - transition: 'all 0.2s ease', - }, + + padding: '12px 16px', + display: 'flex', + alignItems: 'center', + gap: 12, + + background: 'transparent', + borderRadius: 8, + color: '#6b7280', + fontSize: 14, + fontWeight: 500, + textDecoration: 'none', + cursor: 'pointer', + transition: 'all 0.2s ease', + states: { ':hover': { - look: { - background: '#f3f4f6', - color: '#111827', - } + background: '#f3f4f6', + color: '#111827', } } }, Icon: { - layout: { - width: 20, - height: 20, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - look: { - fontSize: 18, - } + width: 20, + height: 20, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontSize: 18, } }, @@ -194,16 +165,12 @@ const VerticalNav = define('VerticalNav', { active: { parts: { NavItem: { - look: { - background: '#eff6ff', - color: '#3b82f6', - }, + background: '#eff6ff', + color: '#3b82f6', states: { ':hover': { - look: { - background: '#dbeafe', - color: '#2563eb', - } + background: '#dbeafe', + color: '#2563eb', } } } @@ -230,43 +197,35 @@ const VerticalNav = define('VerticalNav', { }) const Breadcrumbs = define('Breadcrumbs', { - layout: { - display: 'flex', - alignItems: 'center', - gap: 8, - flexWrap: 'wrap', - }, + display: 'flex', + alignItems: 'center', + gap: 8, + flexWrap: 'wrap', parts: { Item: { base: 'a', - look: { - color: '#6b7280', - fontSize: 14, - textDecoration: 'none', - transition: 'color 0.2s ease', - }, + + color: '#6b7280', + fontSize: 14, + textDecoration: 'none', + transition: 'color 0.2s ease', + states: { ':hover': { - look: { - color: '#3b82f6', - } + color: '#3b82f6', } } }, Separator: { - look: { - color: '#d1d5db', - fontSize: 14, - userSelect: 'none', - } + color: '#d1d5db', + fontSize: 14, + userSelect: 'none', }, Current: { - look: { - color: '#111827', - fontSize: 14, - fontWeight: 500, - } + color: '#111827', + fontSize: 14, + fontWeight: 500, } }, diff --git a/examples/profile.tsx b/examples/profile.tsx index a79fdfc..7422174 100644 --- a/examples/profile.tsx +++ b/examples/profile.tsx @@ -4,167 +4,129 @@ import { Layout, ExampleSection } from './helpers' const UserProfile = define('UserProfile', { base: 'div', - layout: { - padding: 24, - maxWidth: 600, - margin: "0 auto", - }, - - look: { - background: "white", - borderRadius: 12, - boxShadow: "0 2px 8px rgba(0,0,0,0.1)", - }, + padding: 24, + maxWidth: 600, + margin: "0 auto", + background: "white", + borderRadius: 12, + boxShadow: "0 2px 8px rgba(0,0,0,0.1)", parts: { Header: { - layout: { - display: "flex", - alignItems: "center", - gap: 16, - marginBottom: 16, - }, + display: "flex", + alignItems: "center", + gap: 16, + marginBottom: 16, }, Avatar: { base: 'img', - layout: { - width: 64, - height: 64, - }, - look: { - borderRadius: "50%", - objectFit: "cover", - border: "3px solid #e5e7eb", - }, + width: 64, + height: 64, + borderRadius: "50%", + objectFit: "cover", + border: "3px solid #e5e7eb", }, Info: { - layout: { - flex: 1, - }, + flex: 1, }, Name: { - layout: { - marginBottom: 4, - }, - look: { - fontSize: 20, - fontWeight: 600, - color: "#111827", - }, + marginBottom: 4, + fontSize: 20, + fontWeight: 600, + color: "#111827", }, Handle: { - look: { - fontSize: 14, - color: "#6b7280", - }, + fontSize: 14, + color: "#6b7280", }, Bio: { - layout: { - marginBottom: 16, - width: "100%", - }, - look: { - fontSize: 14, - lineHeight: 1.6, - color: "#374151", - wordWrap: "break-word", - }, + marginBottom: 16, + width: "100%", + fontSize: 14, + lineHeight: 1.6, + color: "#374151", + wordWrap: "break-word", }, Stats: { - layout: { - display: "flex", - gap: 24, - paddingTop: 16, - }, - look: { - borderTop: "1px solid #e5e7eb", - }, + display: "flex", + gap: 24, + paddingTop: 16, + borderTop: "1px solid #e5e7eb", }, Stat: { - layout: { - display: "flex", - flexDirection: "column", - gap: 4, - }, + display: "flex", + flexDirection: "column", + gap: 4, }, StatValue: { - look: { - fontSize: 18, - fontWeight: 600, - color: "#111827", - }, + fontSize: 18, + fontWeight: 600, + color: "#111827", }, StatLabel: { - look: { - fontSize: 12, - color: "#6b7280", - textTransform: "uppercase", - }, + fontSize: 12, + color: "#6b7280", + textTransform: "uppercase", }, }, variants: { size: { compact: { - layout: { padding: 16, maxWidth: 300 }, + padding: 16, + maxWidth: 300, parts: { - Avatar: { layout: { width: 48, height: 48 } }, - Name: { look: { fontSize: 16 } }, - Bio: { look: { fontSize: 13 } }, + Avatar: { width: 48, height: 48 }, + Name: { fontSize: 16 }, + Bio: { fontSize: 13 }, }, }, skinny: { - layout: { padding: 20, maxWidth: 125 }, + padding: 20, + maxWidth: 125, parts: { Header: { - layout: { - flexDirection: "column", - alignItems: "center", - gap: 12, - } + flexDirection: "column", + alignItems: "center", + gap: 12, }, - Avatar: { layout: { width: 80, height: 80 } }, - Info: { layout: { flex: 0, width: "100%" } }, - Name: { look: { textAlign: "center", fontSize: 18 } }, - Handle: { look: { textAlign: "center" } }, - Bio: { look: { textAlign: "center", fontSize: 13 } }, + Avatar: { width: 80, height: 80 }, + Info: { flex: 0, width: "100%" }, + Name: { textAlign: "center", fontSize: 18 }, + Handle: { textAlign: "center" }, + Bio: { textAlign: "center", fontSize: 13 }, Stats: { - layout: { - flexDirection: "column", - gap: 16, - } + flexDirection: "column", + gap: 16, }, - Stat: { layout: { alignItems: "center" } }, + Stat: { alignItems: "center" }, }, }, large: { - layout: { padding: 32, maxWidth: 800 }, + padding: 32, + maxWidth: 800, parts: { - Avatar: { layout: { width: 96, height: 96 } }, - Name: { look: { fontSize: 24 } }, + Avatar: { width: 96, height: 96 }, + Name: { fontSize: 24 }, }, }, }, verified: { parts: { Avatar: { - look: { - border: "3px solid #3b82f6", - }, + border: "3px solid #3b82f6", }, }, }, theme: { dark: { - look: { - background: "#1f2937", - }, + background: "#1f2937", parts: { - Name: { look: { color: "#f9fafb" } }, - Handle: { look: { color: "#9ca3af" } }, - Bio: { look: { color: "#d1d5db" } }, - Stats: { look: { borderTop: "1px solid #374151" } }, - StatValue: { look: { color: "#f9fafb" } }, + Name: { color: "#f9fafb" }, + Handle: { color: "#9ca3af" }, + Bio: { color: "#d1d5db" }, + Stats: { borderTop: "1px solid #374151" }, + StatValue: { color: "#f9fafb" }, }, }, }, diff --git a/src/index.tsx b/src/index.tsx index 107ec27..c75339c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,5 @@ import type { JSX } from 'hono/jsx' -import { type TagDef, UnitlessProps } from './types' +import { type TagDef, UnitlessProps, NonStyleKeys } from './types' export const styles: Record> = {} export const Styles = () =>