Add storefront component migration: cards, badges, progress bars, animations, and React wrapper package
Major feature migration from dt-shopify-storefront into the design system: - Card system: per-card color modes (normal/emphasis/warning/success/other), progress bars, full-bleed card body (card-body-flush), image bevel clip-paths, and visible structural borders at bevel corners - Badge system: dt-card-badge with position-aware offsets for cards vs media frames - Media frames: dt-bevel-media with inner surface fill, image clipping, and "MISSING MEDIA" placeholder for empty containers - Interactive bevel buttons: dt-btn with hover/selected states and pulse animation - Menu items: dt-menu-item with active states and level indentation - Animations: scale-in, fade-in, slide-up, pulse, ping, spin keyframes - Feature legend: CSS grid for product feature icons with state colors - Scrollbar styling for DT brand - New @dangerousthings/react package wrapping web CSS components - New @dangerousthings/tailwind-preset package - New @dangerousthings/hex-background package - Desktop showcase rewritten in React with Tailwind CSS - Mobile showcase updated with new screens (animations, filters, advanced cards) - Tokens: added mode color tokens, RGB variants, and selected-state tokens Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
100
packages/showcase/desktop/src/renderer/App.tsx
Normal file
100
packages/showcase/desktop/src/renderer/App.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { themes } from '@dangerousthings/tokens';
|
||||
import type { ThemeBrand, ThemeMode } from '@dangerousthings/tokens';
|
||||
import { HomePage } from './pages/HomePage';
|
||||
import { BevelsPage } from './pages/BevelsPage';
|
||||
import { GlowsPage } from './pages/GlowsPage';
|
||||
import { FormsPage } from './pages/FormsPage';
|
||||
import { AnimationsPage } from './pages/AnimationsPage';
|
||||
import { CardsAdvancedPage } from './pages/CardsAdvancedPage';
|
||||
import { TokensPage } from './pages/TokensPage';
|
||||
|
||||
const navPages = [
|
||||
{ hash: '', label: 'Home' },
|
||||
{ hash: 'bevels', label: 'Bevels' },
|
||||
{ hash: 'glows', label: 'Glows' },
|
||||
{ hash: 'forms', label: 'Forms' },
|
||||
{ hash: 'animations', label: 'Animations' },
|
||||
{ hash: 'cards-advanced', label: 'Advanced Cards' },
|
||||
{ hash: 'tokens', label: 'Tokens' },
|
||||
];
|
||||
|
||||
export function App() {
|
||||
const [brand, setBrand] = useState<ThemeBrand>('dt');
|
||||
const [theme, setTheme] = useState<ThemeMode>('dark');
|
||||
const [currentHash, setCurrentHash] = useState('');
|
||||
|
||||
// Sync brand/theme to <html> so CSS custom properties cascade everywhere
|
||||
useEffect(() => {
|
||||
document.documentElement.setAttribute('data-brand', brand);
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
}, [brand, theme]);
|
||||
|
||||
// Hash-based routing
|
||||
useEffect(() => {
|
||||
function onHashChange() {
|
||||
setCurrentHash(window.location.hash.replace('#/', '').replace('#', ''));
|
||||
}
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
onHashChange();
|
||||
return () => window.removeEventListener('hashchange', onHashChange);
|
||||
}, []);
|
||||
|
||||
function renderPage() {
|
||||
switch (currentHash) {
|
||||
case 'bevels': return <BevelsPage />;
|
||||
case 'glows': return <GlowsPage />;
|
||||
case 'forms': return <FormsPage />;
|
||||
case 'animations': return <AnimationsPage />;
|
||||
case 'cards-advanced': return <CardsAdvancedPage />;
|
||||
case 'tokens': return <TokensPage brand={brand} />;
|
||||
default: return <HomePage />;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav id="sidebar">
|
||||
<div className="nav-title">DT DESIGN SYSTEM</div>
|
||||
<div className="brand-switcher-container">
|
||||
{themes.map(t => (
|
||||
<button
|
||||
key={t.id}
|
||||
className={`brand-btn${t.id === brand ? ' active' : ''}`}
|
||||
onClick={() => setBrand(t.id)}
|
||||
type="button"
|
||||
>
|
||||
{t.name}
|
||||
</button>
|
||||
))}
|
||||
<div className="mode-switcher">
|
||||
{(['dark', 'light', 'auto'] as ThemeMode[]).map(m => (
|
||||
<button
|
||||
key={m}
|
||||
className={`mode-btn${m === theme ? ' active' : ''}`}
|
||||
onClick={() => setTheme(m)}
|
||||
type="button"
|
||||
>
|
||||
{m}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="nav-links">
|
||||
{navPages.map(p => (
|
||||
<a
|
||||
key={p.hash}
|
||||
href={`#/${p.hash}`}
|
||||
className={`nav-link${p.hash === currentHash ? ' active' : ''}`}
|
||||
>
|
||||
{p.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
<main id="content">
|
||||
{renderPage()}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user