initial commit
This commit is contained in:
BIN
packages/web/fonts/Tektur-VariableFont_wdth,wght.ttf
Normal file
BIN
packages/web/fonts/Tektur-VariableFont_wdth,wght.ttf
Normal file
Binary file not shown.
30
packages/web/package.json
Normal file
30
packages/web/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@dangerousthings/web",
|
||||
"version": "0.1.0",
|
||||
"description": "Web CSS theme for the Dangerous Things design system — bevels, glows, form styles",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.css",
|
||||
"exports": {
|
||||
".": "./dist/index.css",
|
||||
"./tokens/*": "./dist/tokens/*",
|
||||
"./components/*": "./dist/components/*",
|
||||
"./fonts/*": "./dist/fonts/*",
|
||||
"./theme-registry": {
|
||||
"types": "./dist/theme-registry.d.ts",
|
||||
"import": "./dist/theme-registry.js",
|
||||
"default": "./dist/theme-registry.js"
|
||||
},
|
||||
"./dist/*": "./dist/*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "node scripts/build.js",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dangerousthings/tokens": "*"
|
||||
}
|
||||
}
|
||||
87
packages/web/scripts/build.js
Normal file
87
packages/web/scripts/build.js
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* @dangerousthings/web build script
|
||||
*
|
||||
* 1. Copies src/ to dist/ (component CSS, base tokens, index.css)
|
||||
* 2. Copies fonts/ to dist/fonts/
|
||||
* 3. Copies generated token CSS from @dangerousthings/tokens/dist/css/ into dist/tokens/
|
||||
* 4. Copies theme-registry from @dangerousthings/tokens
|
||||
*
|
||||
* Consumers (Vite, webpack) resolve @import chains at build time.
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const root = path.join(__dirname, "..");
|
||||
const src = path.join(root, "src");
|
||||
const dist = path.join(root, "dist");
|
||||
const fontsDir = path.join(root, "fonts");
|
||||
|
||||
// Resolve @dangerousthings/tokens dist directory
|
||||
const tokensDir = path.join(root, "..", "tokens", "dist");
|
||||
|
||||
function copyDir(srcDir, destDir) {
|
||||
fs.mkdirSync(destDir, { recursive: true });
|
||||
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
||||
const srcPath = path.join(srcDir, entry.name);
|
||||
const destPath = path.join(destDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
copyDir(srcPath, destPath);
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean dist
|
||||
if (fs.existsSync(dist)) {
|
||||
fs.rmSync(dist, { recursive: true });
|
||||
}
|
||||
|
||||
// 1. Copy src → dist
|
||||
copyDir(src, dist);
|
||||
|
||||
// 2. Copy fonts → dist/fonts
|
||||
if (fs.existsSync(fontsDir)) {
|
||||
copyDir(fontsDir, path.join(dist, "fonts"));
|
||||
}
|
||||
|
||||
// 3. Copy generated token CSS from @dangerousthings/tokens → dist/tokens/
|
||||
const tokensCssDir = path.join(tokensDir, "css");
|
||||
if (fs.existsSync(tokensCssDir)) {
|
||||
const destTokens = path.join(dist, "tokens");
|
||||
fs.mkdirSync(destTokens, { recursive: true });
|
||||
for (const file of fs.readdirSync(tokensCssDir)) {
|
||||
if (file.endsWith(".css")) {
|
||||
fs.copyFileSync(
|
||||
path.join(tokensCssDir, file),
|
||||
path.join(destTokens, file)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error("ERROR: @dangerousthings/tokens CSS not found at", tokensCssDir);
|
||||
console.error("Run: npm run build -w packages/tokens first");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// 4. Copy theme-registry from tokens
|
||||
const registryFiles = ["theme-registry.js", "theme-registry.d.ts"];
|
||||
// The theme-registry is now in @dangerousthings/tokens as index.js — consumers
|
||||
// import types from @dangerousthings/tokens directly. We generate a thin re-export
|
||||
// for backward compatibility with dt-web-theme consumers.
|
||||
const reExport = `// Re-export from @dangerousthings/tokens for backward compatibility
|
||||
export { themes, brands } from "@dangerousthings/tokens";
|
||||
export { DEFAULT_THEME } from "@dangerousthings/tokens";
|
||||
`;
|
||||
const reExportDts = `export { ThemeDefinition, ThemeBrand, ThemeMode } from "@dangerousthings/tokens";
|
||||
export declare const themes: import("@dangerousthings/tokens").ThemeDefinition[];
|
||||
export declare const brands: Record<string, import("@dangerousthings/tokens").BrandTokens>;
|
||||
export declare const DEFAULT_THEME: string;
|
||||
`;
|
||||
fs.writeFileSync(path.join(dist, "theme-registry.js"), reExport);
|
||||
fs.writeFileSync(path.join(dist, "theme-registry.d.ts"), reExportDts);
|
||||
|
||||
console.log("@dangerousthings/web: built to dist/");
|
||||
31
packages/web/src/base.css
Normal file
31
packages/web/src/base.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/* @dangerousthings/web: Base Tokens (shared across all brands) */
|
||||
|
||||
:root {
|
||||
/* Spacing (4px base) */
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
--space-4: 1rem;
|
||||
--space-6: 1.5rem;
|
||||
--space-8: 2rem;
|
||||
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms ease;
|
||||
--transition: 250ms ease;
|
||||
|
||||
/* Font stacks — brands override --font-heading and --font-body */
|
||||
--font-mono: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, monospace;
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
--font-heading: var(--font-sans);
|
||||
--font-body: var(--font-sans);
|
||||
|
||||
/* Border Radius — brands override these */
|
||||
--radius-sm: 0.25rem;
|
||||
--radius: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
|
||||
/* Bevel sizes — DT sets real values; Classic/Supra leave at 0 */
|
||||
--bevel-sm: 0px;
|
||||
--bevel-md: 0px;
|
||||
--bevel-lg: 0px;
|
||||
}
|
||||
290
packages/web/src/components/bevels.css
Normal file
290
packages/web/src/components/bevels.css
Normal file
@@ -0,0 +1,290 @@
|
||||
/* dt-web-theme: Bevel Utilities */
|
||||
/* Angular/beveled clip-path patterns from the DT design system */
|
||||
/* Source: dt-shopify-storefront clip-path patterns + react-native-dt-theme bevelPaths.ts */
|
||||
/* */
|
||||
/* clip-path clips CSS borders and box-shadow. This file uses the storefront's */
|
||||
/* dual-element technique: the element's own background serves as the "border" color, */
|
||||
/* and a ::before pseudo-element provides the inner surface fill. The gap between */
|
||||
/* them follows the clip-path shape perfectly. */
|
||||
/* */
|
||||
/* For glow effects on clipped elements, use filter: drop-shadow() (not box-shadow). */
|
||||
|
||||
/* ============================================================================
|
||||
Card Bevels — dual bottom bevels (bottom-right large + bottom-left small)
|
||||
Source: storefront .card-container + .clip-corners-inner pattern
|
||||
Outer bg = border color, ::before = surface fill
|
||||
Border width: ~3px uniform (storefront --card-border-width: 0.2em)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .dt-bevel-card,
|
||||
[data-brand="dt"] .card {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
background: var(--color-primary);
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
100% 0%,
|
||||
100% calc(100% - var(--bevel-md)),
|
||||
calc(100% - var(--bevel-md)) 100%,
|
||||
var(--bevel-sm) 100%,
|
||||
0% calc(100% - var(--bevel-sm))
|
||||
);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .dt-bevel-card::before,
|
||||
[data-brand="dt"] .card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--color-surface);
|
||||
z-index: -1;
|
||||
/* Inner clip-path mirrors outer shape, inset 3px on all edges.
|
||||
This creates a uniform cyan border along both straight edges AND bevel diagonals. */
|
||||
clip-path: polygon(
|
||||
3px 3px,
|
||||
calc(100% - 3px) 3px,
|
||||
calc(100% - 3px) calc(100% - var(--bevel-md)),
|
||||
calc(100% - var(--bevel-md)) calc(100% - 3px),
|
||||
var(--bevel-sm) calc(100% - 3px),
|
||||
3px calc(100% - var(--bevel-sm))
|
||||
);
|
||||
}
|
||||
|
||||
/* Card Header — storefront .card-header pattern
|
||||
Title extends edge-to-edge on the card's primary bg (cyan).
|
||||
Below it, the ::before dark surface shows through for the body area. */
|
||||
[data-brand="dt"] .card > .card-title,
|
||||
[data-brand="dt"] .dt-bevel-card > .card-title {
|
||||
/* Pull title to card edges, canceling card padding */
|
||||
margin: calc(-1 * var(--space-6)) calc(-1 * var(--space-6)) var(--space-4) calc(-1 * var(--space-6));
|
||||
padding: 1rem var(--space-6);
|
||||
/* Sits above ::before so its own bg covers the dark surface */
|
||||
background: var(--color-primary);
|
||||
color: var(--color-bg);
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Force icon spans to inherit header color (overrides inline styles) */
|
||||
[data-brand="dt"] .card > .card-title > * {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Button Bevels — top-right corner cut
|
||||
Source: storefront .label-clipped pattern (solid fill + clip-path)
|
||||
Filled buttons (primary/danger): direct clip-path, solid bg
|
||||
Outline button (secondary): dual-element technique via ::before
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .btn-primary,
|
||||
[data-brand="dt"] .btn-danger {
|
||||
border: none;
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
calc(100% - var(--bevel-sm)) 0%,
|
||||
100% var(--bevel-sm),
|
||||
100% 100%,
|
||||
0% 100%
|
||||
);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-secondary {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
background: var(--color-primary);
|
||||
border: none;
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
calc(100% - var(--bevel-sm)) 0%,
|
||||
100% var(--bevel-sm),
|
||||
100% 100%,
|
||||
0% 100%
|
||||
);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-secondary::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--color-bg);
|
||||
z-index: -1;
|
||||
clip-path: polygon(
|
||||
2px 2px,
|
||||
calc(100% - var(--bevel-sm)) 2px,
|
||||
calc(100% - 2px) var(--bevel-sm),
|
||||
calc(100% - 2px) calc(100% - 2px),
|
||||
2px calc(100% - 2px)
|
||||
);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-secondary:hover {
|
||||
background: var(--color-accent);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-secondary:hover::before {
|
||||
background: rgba(var(--color-primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Badge/Label Bevels — top-right bevel
|
||||
Source: storefront .label-clipped pattern
|
||||
Dual-element technique: badge bg = border color, ::before = inner fill
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .badge {
|
||||
--badge-border-color: var(--color-border);
|
||||
--badge-fill: var(--color-surface);
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
background: var(--badge-border-color);
|
||||
color: var(--color-text-primary);
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
calc(100% - var(--bevel-sm)) 0%,
|
||||
100% var(--bevel-sm),
|
||||
100% 100%,
|
||||
0% 100%
|
||||
);
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
[data-brand="dt"] .badge::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: var(--badge-fill);
|
||||
z-index: -1;
|
||||
clip-path: polygon(
|
||||
2px 2px,
|
||||
calc(100% - var(--bevel-sm)) 2px,
|
||||
calc(100% - 2px) var(--bevel-sm),
|
||||
calc(100% - 2px) calc(100% - 2px),
|
||||
2px calc(100% - 2px)
|
||||
);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .badge-success {
|
||||
--badge-border-color: var(--color-success);
|
||||
--badge-fill: rgba(var(--color-success-rgb), 0.25);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .badge-error {
|
||||
--badge-border-color: var(--color-error);
|
||||
--badge-fill: rgba(var(--color-error-rgb), 0.25);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .badge-warning {
|
||||
--badge-border-color: var(--color-warning);
|
||||
--badge-fill: rgba(var(--color-warning-rgb), 0.25);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .badge-info {
|
||||
--badge-border-color: var(--color-info);
|
||||
--badge-fill: rgba(var(--color-info-rgb), 0.25);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Opt-in Bevel Utility Classes
|
||||
Use these for custom elements that need bevel shapes
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .dt-bevel-btn {
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
calc(100% - var(--bevel-sm)) 0%,
|
||||
100% var(--bevel-sm),
|
||||
100% 100%,
|
||||
0% 100%
|
||||
);
|
||||
}
|
||||
|
||||
[data-brand="dt"] .dt-bevel-label {
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
calc(100% - var(--bevel-sm)) 0%,
|
||||
100% var(--bevel-sm),
|
||||
100% 100%,
|
||||
0% 100%
|
||||
);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Media Frame Bevels — diagonal opposite corners (top-left + bottom-right)
|
||||
Source: storefront .media-frame-clipped pattern
|
||||
============================================================================ */
|
||||
.dt-bevel-media {
|
||||
clip-path: polygon(
|
||||
var(--bevel-md) 0%,
|
||||
100% 0%,
|
||||
100% calc(100% - var(--bevel-md)),
|
||||
calc(100% - var(--bevel-md)) 100%,
|
||||
0% 100%,
|
||||
0% var(--bevel-md)
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Modal/Drawer Bevels — dual bottom bevels (bottom-right + bottom-left)
|
||||
============================================================================ */
|
||||
.dt-bevel-modal {
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
100% 0%,
|
||||
100% calc(100% - var(--bevel-lg)),
|
||||
calc(100% - var(--bevel-lg)) 100%,
|
||||
var(--bevel-lg) 100%,
|
||||
0% calc(100% - var(--bevel-lg))
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Drawer Bevels — exposed-edge bevels
|
||||
Source: storefront aside clip-path pattern
|
||||
============================================================================ */
|
||||
.dt-bevel-drawer-right {
|
||||
clip-path: polygon(
|
||||
0% var(--bevel-lg),
|
||||
var(--bevel-lg) 0%,
|
||||
100% 0%,
|
||||
100% 100%,
|
||||
var(--bevel-lg) 100%,
|
||||
0% calc(100% - var(--bevel-lg))
|
||||
);
|
||||
}
|
||||
|
||||
.dt-bevel-drawer-left {
|
||||
clip-path: polygon(
|
||||
0% 0%,
|
||||
calc(100% - var(--bevel-lg)) 0%,
|
||||
100% var(--bevel-lg),
|
||||
100% calc(100% - var(--bevel-lg)),
|
||||
calc(100% - var(--bevel-lg)) 100%,
|
||||
0% 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Small Bevels — for compact elements (arrows, stepper buttons)
|
||||
============================================================================ */
|
||||
.dt-bevel-sm {
|
||||
clip-path: polygon(
|
||||
var(--bevel-sm) 0%,
|
||||
100% 0%,
|
||||
100% calc(100% - var(--bevel-sm)),
|
||||
calc(100% - var(--bevel-sm)) 100%,
|
||||
0% 100%,
|
||||
0% var(--bevel-sm)
|
||||
);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Accordion/Menu — thick top border accent (storefront menu-item-clipped)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .dt-accent-top,
|
||||
[data-brand="dt"] .terminal {
|
||||
border-top: 3px solid var(--color-primary);
|
||||
}
|
||||
96
packages/web/src/components/elevation.css
Normal file
96
packages/web/src/components/elevation.css
Normal file
@@ -0,0 +1,96 @@
|
||||
/* dt-web-theme: Supra Elevation & MD3 Styles */
|
||||
/* Source: react-native-supra-theme/src/theme/paperTheme.ts */
|
||||
/* Rounded Material Design 3 aesthetic with elevation shadows */
|
||||
|
||||
/* ============================================================================
|
||||
Card Elevation
|
||||
============================================================================ */
|
||||
[data-brand="supra"] .card {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
[data-brand="supra"] .card:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Button — Dual-layer inset shadow (hardware button feel)
|
||||
Source: SupraButton.tsx — inner shadow for tactile appearance
|
||||
============================================================================ */
|
||||
[data-brand="supra"] .btn-primary {
|
||||
box-shadow:
|
||||
inset -2px -2px 4px rgba(0, 0, 0, 0.18),
|
||||
inset 2px 2px 4px rgba(255, 255, 255, 0.25);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
[data-brand="supra"] .btn-primary:hover {
|
||||
box-shadow:
|
||||
inset -2px -2px 6px rgba(0, 0, 0, 0.22),
|
||||
inset 2px 2px 6px rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
[data-brand="supra"] .btn-secondary {
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
[data-brand="supra"] .btn-danger {
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Badge — Rounded pill (MD3 chip style)
|
||||
============================================================================ */
|
||||
[data-brand="supra"] .badge {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Input — Rounded with standard focus ring
|
||||
============================================================================ */
|
||||
[data-brand="supra"] .input,
|
||||
[data-brand="supra"] input,
|
||||
[data-brand="supra"] textarea,
|
||||
[data-brand="supra"] select {
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
[data-brand="supra"] .input:focus,
|
||||
[data-brand="supra"] input:focus,
|
||||
[data-brand="supra"] textarea:focus,
|
||||
[data-brand="supra"] select:focus {
|
||||
box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.2);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Terminal — Rounded with subtle elevation
|
||||
============================================================================ */
|
||||
[data-brand="supra"] .terminal {
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Elevation Utility Classes
|
||||
============================================================================ */
|
||||
.supra-elevation-1 {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.supra-elevation-2 {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.supra-elevation-3 {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.22);
|
||||
}
|
||||
|
||||
.supra-elevation-4 {
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.supra-elevation-5 {
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
329
packages/web/src/components/forms-dt.css
Normal file
329
packages/web/src/components/forms-dt.css
Normal file
@@ -0,0 +1,329 @@
|
||||
/* dt-web-theme: DT Form Component Styles */
|
||||
/* Source: react-native-dt-theme/src/components/DT*.tsx */
|
||||
/* These styles exist in the RN DT theme but NOT the Shopify storefront */
|
||||
|
||||
/* ============================================================================
|
||||
Text Input — Sharp corners + focus glow bar
|
||||
Source: DTTextInput.tsx
|
||||
============================================================================ */
|
||||
[data-brand="dt"] input[type="text"],
|
||||
[data-brand="dt"] input[type="password"],
|
||||
[data-brand="dt"] input[type="email"],
|
||||
[data-brand="dt"] input[type="number"],
|
||||
[data-brand="dt"] input[type="search"],
|
||||
[data-brand="dt"] input[type="url"],
|
||||
[data-brand="dt"] input[type="tel"],
|
||||
[data-brand="dt"] textarea,
|
||||
[data-brand="dt"] select,
|
||||
[data-brand="dt"] .input {
|
||||
border: 2px solid var(--color-primary);
|
||||
border-radius: 0;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text-primary);
|
||||
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
||||
}
|
||||
|
||||
[data-brand="dt"] input:focus,
|
||||
[data-brand="dt"] textarea:focus,
|
||||
[data-brand="dt"] select:focus,
|
||||
[data-brand="dt"] .input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 4px 0 1px var(--color-primary);
|
||||
}
|
||||
|
||||
[data-brand="dt"] input.error,
|
||||
[data-brand="dt"] .input.error {
|
||||
border-color: var(--color-error);
|
||||
}
|
||||
|
||||
[data-brand="dt"] input.error:focus,
|
||||
[data-brand="dt"] .input.error:focus {
|
||||
box-shadow: 0 4px 0 1px var(--color-error);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Checkbox — Beveled opposing corners (top-left + bottom-right)
|
||||
Source: DTCheckbox.tsx — bevel at 30% of size
|
||||
Uses drop-shadow instead of border (clip-path clips CSS borders)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] input[type="checkbox"] {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
background: rgba(var(--color-primary-rgb), 0.1);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
clip-path: polygon(
|
||||
30% 0%, 100% 0%, 100% 70%, 70% 100%, 0% 100%, 0% 30%
|
||||
);
|
||||
filter: drop-shadow(0 0 1px var(--color-primary));
|
||||
transition: background var(--transition-fast), filter var(--transition-fast);
|
||||
}
|
||||
|
||||
[data-brand="dt"] input[type="checkbox"]:checked {
|
||||
background: var(--color-primary);
|
||||
filter: drop-shadow(0 0 2px var(--color-primary));
|
||||
}
|
||||
|
||||
[data-brand="dt"] input[type="checkbox"]:checked::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
top: 8px;
|
||||
width: 10px;
|
||||
height: 6px;
|
||||
border: 2px solid var(--color-bg);
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
[data-brand="dt"] input[type="checkbox"]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Switch / Toggle — Angular track + sliding square thumb
|
||||
Source: DTSwitch.tsx — 48x26 track, 20x20 thumb, 150ms
|
||||
============================================================================ */
|
||||
.dt-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 48px;
|
||||
height: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dt-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.dt-switch-track {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border: 2px solid var(--color-primary);
|
||||
border-radius: 0;
|
||||
background: rgba(var(--color-primary-rgb), 0.1);
|
||||
transition: background var(--transition-fast);
|
||||
}
|
||||
|
||||
.dt-switch input:checked + .dt-switch-track {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.dt-switch-thumb {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: #ffffff;
|
||||
transition: left var(--transition-fast);
|
||||
}
|
||||
|
||||
.dt-switch input:checked ~ .dt-switch-thumb,
|
||||
.dt-switch input:checked + .dt-switch-track + .dt-switch-thumb {
|
||||
left: 25px;
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
.dt-switch input:disabled + .dt-switch-track {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Radio Button — Hexagonal indicator
|
||||
Source: DTRadioGroup.tsx — flat-top hexagon, 22px default
|
||||
Uses drop-shadow instead of border (clip-path clips CSS borders)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] input[type="radio"] {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: none;
|
||||
background: rgba(var(--color-primary-rgb), 0.1);
|
||||
cursor: pointer;
|
||||
clip-path: polygon(
|
||||
50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%
|
||||
);
|
||||
filter: drop-shadow(0 0 1px var(--color-primary));
|
||||
transition: background var(--transition-fast), filter var(--transition-fast);
|
||||
}
|
||||
|
||||
[data-brand="dt"] input[type="radio"]:checked {
|
||||
background: var(--color-primary);
|
||||
filter: drop-shadow(0 0 2px var(--color-primary));
|
||||
}
|
||||
|
||||
[data-brand="dt"] input[type="radio"]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Radio group option container */
|
||||
.dt-radio-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
border: 2px solid rgba(var(--color-primary-rgb), 0.3);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.dt-radio-option.selected {
|
||||
background: rgba(var(--color-primary-rgb), 0.15);
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Progress Bar — Angular, no radius
|
||||
Source: DTProgressBar.tsx — 4px height, no border-radius
|
||||
============================================================================ */
|
||||
.dt-progress {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dt-progress-fill {
|
||||
height: 100%;
|
||||
background: var(--color-primary);
|
||||
transition: width 300ms ease-out;
|
||||
}
|
||||
|
||||
.dt-progress-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
margin-top: 4px;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
/* Vertical variant */
|
||||
.dt-progress.vertical {
|
||||
width: 8px;
|
||||
height: 200px;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.dt-progress.vertical .dt-progress-fill {
|
||||
width: 100%;
|
||||
height: 0%;
|
||||
transition: height 300ms ease-out;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Accordion — 5px top border, chevron rotation
|
||||
Source: DTAccordion.tsx — 250ms animation
|
||||
============================================================================ */
|
||||
.dt-accordion {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.dt-accordion-header {
|
||||
background: var(--color-bg);
|
||||
border: 1px solid var(--color-primary);
|
||||
border-top: 5px solid var(--color-primary);
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-text-primary);
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.dt-accordion-header:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.dt-accordion-header[aria-expanded="true"] {
|
||||
color: var(--color-secondary);
|
||||
border-top-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.dt-accordion-chevron {
|
||||
display: inline-block;
|
||||
transition: transform 250ms ease-in-out;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 7px solid currentColor;
|
||||
}
|
||||
|
||||
.dt-accordion-header[aria-expanded="true"] .dt-accordion-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.dt-accordion-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 250ms ease-in-out;
|
||||
}
|
||||
|
||||
.dt-accordion-content[data-open="true"] {
|
||||
max-height: 2000px;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Quantity Stepper — Beveled +/- buttons
|
||||
Source: DTQuantityStepper.tsx — bottom-right bevel
|
||||
============================================================================ */
|
||||
.dt-stepper {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dt-stepper-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 2px solid var(--color-primary);
|
||||
background: transparent;
|
||||
color: var(--color-primary);
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
transition: opacity var(--transition-fast);
|
||||
}
|
||||
|
||||
.dt-stepper-btn:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.dt-stepper-btn:disabled {
|
||||
opacity: 0.3;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.dt-stepper-value {
|
||||
min-width: 36px;
|
||||
text-align: center;
|
||||
color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
80
packages/web/src/components/glows.css
Normal file
80
packages/web/src/components/glows.css
Normal file
@@ -0,0 +1,80 @@
|
||||
/* dt-web-theme: Glow & Neon Effects */
|
||||
/* Source: dt-shopify-storefront glow patterns */
|
||||
/* */
|
||||
/* Elements with clip-path use filter: drop-shadow() instead of box-shadow */
|
||||
/* because clip-path clips box-shadow but filter follows the clipped shape. */
|
||||
|
||||
/* ============================================================================
|
||||
Button Glows — filter: drop-shadow (buttons have clip-path)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .btn-primary {
|
||||
box-shadow: none;
|
||||
filter: drop-shadow(0 0 10px rgba(var(--color-primary-rgb), 0.3));
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-primary:hover {
|
||||
box-shadow: none;
|
||||
filter: drop-shadow(0 0 15px rgba(var(--color-accent-rgb), 0.4));
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-secondary:hover {
|
||||
filter: drop-shadow(0 0 8px rgba(var(--color-primary-rgb), 0.3));
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-danger {
|
||||
box-shadow: none;
|
||||
filter: drop-shadow(0 0 10px rgba(var(--color-error-rgb), 0.3));
|
||||
}
|
||||
|
||||
[data-brand="dt"] .btn-danger:hover {
|
||||
box-shadow: none;
|
||||
filter: drop-shadow(0 0 15px rgba(var(--color-error-rgb), 0.4));
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Link Glow
|
||||
============================================================================ */
|
||||
[data-brand="dt"] a:hover {
|
||||
text-shadow: 0 0 8px var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Terminal Inset Glow (no clip-path, box-shadow OK)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .terminal {
|
||||
box-shadow: inset 0 0 10px rgba(var(--color-accent-rgb), 0.05),
|
||||
0 0 10px rgba(var(--color-accent-rgb), 0.1);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Card Hover Glow — filter: drop-shadow (cards have clip-path)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .card:hover {
|
||||
filter: drop-shadow(0 0 8px rgba(var(--color-primary-rgb), 0.3));
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Input Focus Glow (no clip-path on inputs, box-shadow OK)
|
||||
============================================================================ */
|
||||
[data-brand="dt"] .input:focus {
|
||||
box-shadow: 0 4px 0 1px var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Generic Glow Utilities
|
||||
============================================================================ */
|
||||
.dt-glow {
|
||||
filter: drop-shadow(0 0 10px rgba(var(--color-primary-rgb), 0.3));
|
||||
}
|
||||
|
||||
.dt-glow-strong {
|
||||
filter: drop-shadow(0 0 15px rgba(var(--color-primary-rgb), 0.5));
|
||||
}
|
||||
|
||||
.dt-glow-inset {
|
||||
box-shadow: inset 0 0 10px rgba(var(--color-primary-rgb), 0.1);
|
||||
}
|
||||
|
||||
.dt-text-glow {
|
||||
text-shadow: 0 0 8px currentColor;
|
||||
}
|
||||
9
packages/web/src/dt-font.css
Normal file
9
packages/web/src/dt-font.css
Normal file
@@ -0,0 +1,9 @@
|
||||
/* @dangerousthings/web: Tektur Font Face */
|
||||
@font-face {
|
||||
font-family: 'Tektur';
|
||||
src: url('../fonts/Tektur-VariableFont_wdth,wght.ttf') format('truetype');
|
||||
font-weight: 100 900;
|
||||
font-stretch: 75% 125%;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
21
packages/web/src/index.css
Normal file
21
packages/web/src/index.css
Normal file
@@ -0,0 +1,21 @@
|
||||
/* @dangerousthings/web: Main Entry Point */
|
||||
/* Import this file to get all tokens and component styles */
|
||||
|
||||
/* Base tokens (spacing, transitions, font stacks) */
|
||||
@import './base.css';
|
||||
|
||||
/* Tektur font face (used by DT brand) */
|
||||
@import './dt-font.css';
|
||||
|
||||
/* Brand tokens — generated from @dangerousthings/tokens */
|
||||
@import './tokens/dt.css';
|
||||
@import './tokens/classic.css';
|
||||
@import './tokens/supra.css';
|
||||
|
||||
/* Component styles — DT visual identity */
|
||||
@import './components/bevels.css';
|
||||
@import './components/glows.css';
|
||||
@import './components/forms-dt.css';
|
||||
|
||||
/* Component styles — Supra/MD3 visual identity */
|
||||
@import './components/elevation.css';
|
||||
Reference in New Issue
Block a user