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:
michael
2026-03-05 13:26:03 -08:00
parent 47e8085581
commit e74c285b70
109 changed files with 7196 additions and 1026 deletions

View File

@@ -52,16 +52,29 @@ import { themes, brands } from "@dangerousthings/web/theme-registry";
| File | Description |
|------|-------------|
| `bevels.css` | Angular clip-path bevels for cards, buttons, labels, modals, drawers |
| `glows.css` | Neon drop-shadow and text-shadow effects for clipped and standard elements |
| `forms-dt.css` | Text inputs, checkboxes, switches, radio buttons, progress bars, accordions, quantity steppers |
| `bevels.css` | Angular clip-path bevels for cards, buttons, labels, modals, drawers. Card color modes, selected states, progress bars, badge overlays, interactive bevel buttons |
| `glows.css` | Neon drop-shadow and text-shadow effects — mode-aware via `--dt-glow-color` |
| `forms-dt.css` | Text inputs, checkboxes, switches, radio buttons, progress bars, accordions, steppers, menu items, filter headers, filter overlays |
| `animations.css` | Entrance animations (scale-in, fade-in, slide-up), interactive animations (pulse, ping, spin), stagger container, transition utilities |
| `scrollbar.css` | Thin neon scrollbar styling scoped under `[data-brand="dt"]` |
| `feature-legend.css` | Product feature grid with icons and rotated labels, state-based coloring |
### Key Classes
**Bevels**`.dt-bevel-card`, `.dt-bevel-btn`, `.dt-bevel-label`, `.dt-bevel-modal`, `.dt-bevel-drawer-left`, `.dt-bevel-drawer-right`, `.dt-bevel-sm`
**Card Modes**`.mode-normal`, `.mode-emphasis`, `.mode-warning`, `.mode-success`, `.mode-other`, `.card.selected`, `.dt-card-progress`, `.dt-badge-overlay`
**Interactive Buttons**`.dt-btn` (outlined rectangle, bevels on hover/select)
**Glows**`.dt-glow`, `.dt-glow-strong`, `.dt-glow-inset`, `.dt-text-glow`
**Animations**`.dt-animate-scale-in`, `.dt-animate-fade-in`, `.dt-animate-slide-up`, `.dt-animate-pulse`, `.dt-animate-ping`, `.dt-animate-spin`, `.dt-stagger-container`
**Scrollbar**`.dt-scrollbar`, `.dt-scrollbar-mode`
**Filters**`.dt-menu-item`, `.dt-filter-header`, `.dt-filter-overlay`
## Exports
| Path | Description |

View File

@@ -28,4 +28,7 @@
--bevel-sm: 0px;
--bevel-md: 0px;
--bevel-lg: 0px;
/* Interactive bevel corner size (buttons/menu items on hover) */
--menu-item-corner: 1em;
}

View File

@@ -0,0 +1,137 @@
/* dt-web-theme: Animation Library */
/* Source: dt-shopify-storefront animation patterns */
/* Brand-independent — no color references in keyframes */
/* ============================================================================
Entrance Animations
============================================================================ */
@keyframes dt-scale-in {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes dt-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes dt-slide-up {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
/* ============================================================================
Interactive Animations
============================================================================ */
@keyframes dt-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes dt-ping {
75%, 100% {
transform: scale(2);
opacity: 0;
}
}
@keyframes dt-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* ============================================================================
Utility Classes
============================================================================ */
.dt-animate-scale-in {
animation: dt-scale-in 0.33s ease-in-out both;
}
.dt-animate-fade-in {
animation: dt-fade-in 0.3s ease-in-out both;
}
.dt-animate-slide-up {
animation: dt-slide-up 0.3s ease-in-out both;
}
.dt-animate-pulse {
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
.dt-animate-ping {
animation: dt-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
}
.dt-animate-spin {
animation: dt-spin 1s linear infinite;
}
/* ============================================================================
Stagger Container
Source: storefront framer-motion staggered card entrance
Usage: <div class="dt-stagger-container">...</div>
Customize: --dt-stagger-duration, --dt-stagger-interval
============================================================================ */
.dt-stagger-container {
--dt-stagger-duration: 0.33s;
--dt-stagger-interval: 75ms;
}
.dt-stagger-container > * {
animation: dt-scale-in var(--dt-stagger-duration) ease-in-out both;
}
.dt-stagger-container > :nth-child(1) { animation-delay: calc(0 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(2) { animation-delay: calc(1 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(3) { animation-delay: calc(2 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(4) { animation-delay: calc(3 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(5) { animation-delay: calc(4 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(6) { animation-delay: calc(5 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(7) { animation-delay: calc(6 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(8) { animation-delay: calc(7 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(9) { animation-delay: calc(8 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(10) { animation-delay: calc(9 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(11) { animation-delay: calc(10 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(12) { animation-delay: calc(11 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(13) { animation-delay: calc(12 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(14) { animation-delay: calc(13 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(15) { animation-delay: calc(14 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(16) { animation-delay: calc(15 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(17) { animation-delay: calc(16 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(18) { animation-delay: calc(17 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(19) { animation-delay: calc(18 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(20) { animation-delay: calc(19 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(21) { animation-delay: calc(20 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(22) { animation-delay: calc(21 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(23) { animation-delay: calc(22 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(24) { animation-delay: calc(23 * var(--dt-stagger-interval)); }
.dt-stagger-container > :nth-child(n+25) {
animation-delay: calc(24 * var(--dt-stagger-interval));
}
/* ============================================================================
Transition Utilities
============================================================================ */
.dt-transition-accordion {
transition: max-height 250ms ease-in-out;
}
.dt-transition-chevron {
transition: transform 250ms ease-in-out;
}
.dt-transition-chevron[aria-expanded="true"],
.dt-transition-chevron.is-expanded {
transform: rotate(180deg);
}
.dt-transition-progress {
transition: height 300ms ease-out, width 300ms ease-out;
}

View File

@@ -17,19 +17,22 @@
============================================================================ */
[data-brand="dt"] .dt-bevel-card,
[data-brand="dt"] .card {
--dt-card-progress: 0;
--_card-pad: var(--space-6);
position: relative;
isolation: isolate;
background: var(--color-primary);
background: var(--dt-card-color, 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))
);
padding: var(--_card-pad);
padding-left: calc(var(--bevel-sm) + var(--_card-pad));
overflow: clip;
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,
@@ -39,41 +42,109 @@
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))
);
/* Inner clip-path: left edge at bevel-sm to create progress bar zone.
The progress bar occupies x=0 to x=bevel-sm with its own borders.
Inner surface starts at bevel-sm, so the left frame is wider than 3px. */
clip-path: polygon(var(--bevel-sm) 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));
}
/* Card Progress Bar — structural fill area on the left edge
Sits between the 3px frame borders, follows bottom-left bevel diagonal.
Gradient fills from bottom (accent) to top (surface) based on --dt-card-progress.
Filled portion: fully opaque accent color. Unfilled portion: semi-transparent surface.
At 0%: all semi-transparent surface. At 100%: all opaque accent.
Override --dt-progress-empty-opacity to control unfilled portion transparency. */
[data-brand="dt"] .dt-bevel-card::after,
[data-brand="dt"] .card::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
clip-path: polygon(3px 3px,
calc(var(--bevel-sm) - 3px) 3px,
calc(var(--bevel-sm) - 3px) calc(100% - 6px),
3px calc(100% - var(--bevel-sm)));
background: linear-gradient(to top,
rgba(var(--dt-card-color-rgb, var(--color-primary-rgb)), 1) calc(var(--dt-card-progress, 0) * 1%),
rgba(var(--color-surface-rgb),
var(--dt-progress-empty-opacity, 0.6)) calc(var(--dt-card-progress, 0) * 1%));
pointer-events: none;
}
/* Card children — ensure body content renders above the progress bar (::after z-index:1) */
[data-brand="dt"] .dt-bevel-card > *:not(.dt-card-badge),
[data-brand="dt"] .card > *:not(.dt-card-badge) {
position: relative;
z-index: 2;
}
/* Card Header — storefront .card-header pattern
Title extends edge-to-edge on the card's primary bg (cyan).
Title starts at bevel-sm (past the progress bar zone) and extends to the right edge.
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);
[data-brand="dt"] .card>.card-title,
[data-brand="dt"] .dt-bevel-card>.card-title {
/* Pull title toward card edges to sit flush inside the 3px frame border.
Uses 2px inset (1px overlap into the frame border) on top/right/left
to prevent sub-pixel anti-aliasing gaps. The overlap is invisible
because the header and frame border share the same accent color.
Left edge lands at bevel-sm - 1px, keeping the progress bar column
visible alongside the header (the progress bar spans full card height). */
margin: calc(2px - var(--_card-pad)) calc(2px - var(--_card-pad)) var(--space-4) calc(-1px - var(--_card-pad));
padding: 1rem calc(var(--_card-pad) + 1px) 1rem calc(var(--_card-pad) + 1px);
/* Sits above ::before so its own bg covers the dark surface */
background: var(--color-primary);
background: var(--dt-card-color, var(--color-primary));
color: var(--color-bg);
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 1rem;
position: relative;
z-index: 2;
}
/* Force icon spans to inherit header color (overrides inline styles) */
[data-brand="dt"] .card > .card-title > * {
[data-brand="dt"] .card>.card-title>* {
color: inherit !important;
}
/* Full-bleed card body — pulls content flush against the inner frame border.
Use for images/media that should fill the card body edge-to-edge.
Margins align with the card's inner surface edges so the card's structural
3px border is the only visible border (no double-border). */
[data-brand="dt"] .card>.card-body-flush,
[data-brand="dt"] .dt-bevel-card>.card-body-flush {
margin: 0 calc(3px - var(--_card-pad)) calc(3px - var(--_card-pad)) calc(-1 * var(--_card-pad));
overflow: hidden;
background: var(--dt-card-color, var(--color-primary));
}
/* Bottom-right bevel on card body content (img/video). Clip-path matches
the card's inner surface bevel so the 3px structural border is visible
at the diagonal corner. Badge is not clipped (it's a sibling, not img). */
[data-brand="dt"] .card-body-flush > img,
[data-brand="dt"] .card-body-flush > video {
display: block;
clip-path: polygon(
0% 0%,
100% 0%,
100% calc(100% - var(--bevel-md) + 3px),
calc(100% - var(--bevel-md) + 3px) 100%,
0% 100%);
}
/* Remove gap between card title and full-bleed body */
[data-brand="dt"] .card>.card-title + .card-body-flush,
[data-brand="dt"] .dt-bevel-card>.card-title + .card-body-flush {
margin-top: calc(-1 * var(--space-4));
}
/* ============================================================================
Button Bevels — top-right corner cut
Source: storefront .label-clipped pattern (solid fill + clip-path)
@@ -83,13 +154,11 @@
[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%
);
clip-path: polygon(0% 0%,
calc(100% - var(--bevel-sm)) 0%,
100% var(--bevel-sm),
100% 100%,
0% 100%);
}
[data-brand="dt"] .btn-secondary {
@@ -97,13 +166,11 @@
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%
);
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 {
@@ -112,13 +179,11 @@
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)
);
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 {
@@ -141,13 +206,11 @@
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%
);
clip-path: polygon(0% 0%,
calc(100% - var(--bevel-sm)) 0%,
100% var(--bevel-sm),
100% 100%,
0% 100%);
border: none;
border-radius: 0;
}
@@ -158,13 +221,11 @@
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)
);
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 {
@@ -204,23 +265,19 @@
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%
);
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%
);
clip-path: polygon(0% 0%,
calc(100% - var(--bevel-sm)) 0%,
100% var(--bevel-sm),
100% 100%,
0% 100%);
border-radius: 0;
}
@@ -229,28 +286,91 @@
Source: storefront .media-frame-clipped pattern
============================================================================ */
.dt-bevel-media {
position: relative;
isolation: isolate;
background: var(--dt-card-color, var(--color-primary));
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));
}
/* Inner surface fill — shows as background when no image is loaded */
.dt-bevel-media::before {
content: '';
position: absolute;
inset: 0;
background: var(--color-surface);
z-index: -1;
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)
);
calc(var(--bevel-md) + 1px) 3px,
calc(100% - 3px) 3px,
calc(100% - 3px) calc(100% - var(--bevel-md) - 1px),
calc(100% - var(--bevel-md) - 1px) calc(100% - 3px),
3px calc(100% - 3px),
3px calc(var(--bevel-md) + 1px));
}
.dt-bevel-media > img,
.dt-bevel-media > video {
display: block;
clip-path: polygon(
calc(var(--bevel-md) + 1px) 3px,
calc(100% - 3px) 3px,
calc(100% - 3px) calc(100% - var(--bevel-md) - 1px),
calc(100% - var(--bevel-md) - 1px) calc(100% - 3px),
3px calc(100% - 3px),
3px calc(var(--bevel-md) + 1px));
}
/* Media frame placeholder — shows when no image/video is present.
Surface-colored background is already provided by ::before.
This adds centered "MISSING MEDIA" text via ::after. */
.dt-bevel-media::after {
content: 'MISSING MEDIA';
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: -1;
color: var(--color-text-muted, #888);
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
pointer-events: none;
}
/* Card body placeholder — shows when image container is empty or image fails.
Uses surface background and centered placeholder text. */
.dt-badge-parent:empty::after,
.card-body-flush:empty::after {
content: 'MISSING MEDIA';
display: flex;
align-items: center;
justify-content: center;
min-height: 120px;
color: var(--color-text-muted, #888);
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
background: var(--color-surface);
}
/* ============================================================================
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))
);
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)));
}
/* ============================================================================
@@ -258,39 +378,33 @@
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))
);
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%
);
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)
);
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));
}
/* ============================================================================
@@ -300,3 +414,156 @@
[data-brand="dt"] .terminal {
border-top: 3px solid var(--color-primary);
}
/* ============================================================================
Card Color Modes — per-instance color overrides
Sets --dt-card-color, --dt-card-color-selected, --dt-card-color-rgb,
--dt-glow-color, and --accent-mode for use by all card sub-components.
Source: dt-shopify-storefront .mode-* classes
============================================================================ */
[data-brand="dt"] .mode-normal {
--dt-card-color: var(--mode-normal);
--dt-card-color-rgb: var(--mode-normal-rgb);
--dt-card-color-selected: var(--mode-normal-selected);
--dt-glow-color: var(--mode-normal);
--accent-mode: var(--mode-emphasis);
}
[data-brand="dt"] .mode-emphasis {
--dt-card-color: var(--mode-emphasis);
--dt-card-color-rgb: var(--mode-emphasis-rgb);
--dt-card-color-selected: var(--mode-emphasis-selected);
--dt-glow-color: var(--mode-emphasis);
--accent-mode: var(--mode-normal);
}
[data-brand="dt"] .mode-warning {
--dt-card-color: var(--mode-warning);
--dt-card-color-rgb: var(--mode-warning-rgb);
--dt-card-color-selected: var(--mode-warning-selected);
--dt-glow-color: var(--mode-warning);
--accent-mode: var(--mode-emphasis);
}
[data-brand="dt"] .mode-success {
--dt-card-color: var(--mode-success);
--dt-card-color-rgb: var(--mode-success-rgb);
--dt-card-color-selected: var(--mode-success-selected);
--dt-glow-color: var(--mode-success);
--accent-mode: var(--mode-emphasis);
}
[data-brand="dt"] .mode-other {
--dt-card-color: var(--mode-other);
--dt-card-color-rgb: var(--mode-other-rgb);
--dt-card-color-selected: var(--mode-other-selected);
--dt-glow-color: var(--mode-other);
--accent-mode: var(--mode-normal);
}
/* ============================================================================
Card Badge — bottom-right product type chip (LAB, BUNDLE, etc.)
Source: dt-shopify-storefront CyberProductCard LabChip / BundleChip
Plain flat rectangle. Inherits card color via --dt-card-color.
============================================================================ */
/* Badge parent — provides position context for absolutely-positioned badges.
Use on the image container inside a card, or on any element that wraps content + badge. */
.dt-badge-parent {
position: relative;
}
.dt-card-badge {
position: absolute;
bottom: 8px;
right: 8px;
z-index: 4;
padding: 2px calc(8px + 0.2rem) 2px 8px;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
background-color: var(--dt-card-color, var(--color-primary));
color: var(--color-bg);
}
/* Inside card body-flush, offset badge position to compensate for the
3px margin from the card edge. This makes the badge position relative
to the card's bevel corner match the media frame's placement exactly
(8px from card edge in both cases). */
.card-body-flush > .dt-card-badge {
bottom: 5px;
right: 5px;
}
/* ============================================================================
Interactive Bevel Button — storefront .menu-item-clipped pattern
Default: outlined rectangle, no clip-path, 5px top border
Hover: bottom-right bevel appears, fills with mode color, pulse animation
Selected: bevel persists, fills with 70% opacity mode color
============================================================================ */
[data-brand="dt"] .dt-btn {
color: var(--accent-mode, var(--color-primary));
background-color: var(--color-bg);
border: 1px solid var(--dt-card-color, var(--color-primary));
border-top: 5px solid var(--dt-card-color, var(--color-primary));
text-decoration: none;
padding: 0.5rem 0.75rem;
position: relative;
display: inline-block;
overflow: visible;
font-weight: 600;
font-size: 0.875rem;
letter-spacing: 0.05em;
text-transform: uppercase;
cursor: pointer;
transition: background-color var(--transition-fast),
color var(--transition-fast),
clip-path var(--transition-fast);
}
[data-brand="dt"] .dt-btn:hover {
clip-path: polygon(0% 0%,
100% 0%,
100% calc(100% - var(--menu-item-corner)),
calc(100% - var(--menu-item-corner)) 100%,
0% 100%);
color: var(--color-bg);
background-color: var(--dt-card-color, var(--color-primary));
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
[data-brand="dt"] .dt-btn.selected {
clip-path: polygon(0% 0%,
100% 0%,
100% calc(100% - var(--menu-item-corner)),
calc(100% - var(--menu-item-corner)) 100%,
0% 100%);
background-color: var(--dt-card-color-selected, rgba(var(--color-primary-rgb), 0.7));
color: var(--color-bg);
animation: none;
}
[data-brand="dt"] .dt-btn.selected:hover {
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Classic brand: no clip-path, uses border-radius instead */
[data-brand="classic"] .dt-btn {
border-radius: var(--radius);
border-top-width: 2px;
}
[data-brand="classic"] .dt-btn:hover {
clip-path: none;
background-color: var(--dt-card-color, var(--color-primary));
color: var(--color-bg);
border-radius: var(--radius);
}
[data-brand="classic"] .dt-btn.selected {
clip-path: none;
background-color: var(--dt-card-color-selected, rgba(var(--color-primary-rgb), 0.7));
color: var(--color-bg);
border-radius: var(--radius);
}

View File

@@ -0,0 +1,56 @@
/* dt-web-theme: Feature Legend Layout */
/* Source: dt-shopify-storefront UseCaseLegend component */
/* ============================================================================
Feature Legend — icon grid with rotated labels
============================================================================ */
.dt-feature-legend {
display: flex;
flex-direction: column;
gap: 0;
}
.dt-feature-legend-header {
padding: 8px 16px;
background: var(--dt-card-color, var(--color-primary));
color: var(--color-bg);
font-weight: 700;
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.dt-feature-legend-grid {
display: flex;
flex-wrap: wrap;
gap: 0;
}
.dt-feature-legend-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 8px 4px;
flex: 0 0 20%; /* 5 columns */
}
.dt-feature-legend-icon {
font-size: 42px;
line-height: 1;
color: var(--dt-feature-color, var(--color-primary));
}
.dt-feature-legend-label {
font-size: 0.75rem;
font-weight: 600;
color: var(--dt-feature-color, var(--color-text-primary));
text-align: center;
line-height: 1.2;
overflow-wrap: break-word;
}
/* Feature state color classes */
.dt-feature-supported { --dt-feature-color: var(--mode-normal, var(--color-primary)); }
.dt-feature-disabled { --dt-feature-color: var(--mode-emphasis, var(--color-secondary)); }
.dt-feature-unsupported { --dt-feature-color: var(--mode-warning, var(--color-error)); }

View File

@@ -327,3 +327,135 @@
font-weight: 700;
font-family: var(--font-mono);
}
/* ============================================================================
Menu Item — beveled clipped style with thick top border
Source: dt-shopify-storefront .menu-item-clipped pattern
============================================================================ */
[data-brand="dt"] .dt-menu-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0.75rem;
border: 1px solid var(--dt-card-color, var(--color-primary));
border-top: 5px solid var(--dt-card-color, var(--color-primary));
background: var(--color-bg);
color: var(--accent-mode, var(--color-primary));
font-weight: 600;
font-size: 0.875rem;
letter-spacing: 0.05em;
text-transform: uppercase;
cursor: pointer;
transition: all var(--transition-fast);
}
[data-brand="dt"] .dt-menu-item:hover {
clip-path: polygon(
0% 0%,
100% 0%,
100% calc(100% - var(--menu-item-corner)),
calc(100% - var(--menu-item-corner)) 100%,
0% 100%
);
background-color: var(--dt-card-color, var(--color-primary));
color: var(--color-bg);
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
[data-brand="dt"] .dt-menu-item.active,
[data-brand="dt"] .dt-menu-item[aria-expanded="true"] {
color: var(--color-secondary);
border-color: var(--color-secondary);
}
[data-brand="dt"] .dt-menu-item.selected {
clip-path: polygon(
0% 0%,
100% 0%,
100% calc(100% - var(--menu-item-corner)),
calc(100% - var(--menu-item-corner)) 100%,
0% 100%
);
background-color: var(--dt-card-color-selected, rgba(var(--color-primary-rgb), 0.7));
color: var(--color-bg);
animation: none;
}
[data-brand="dt"] .dt-menu-item.selected:hover {
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Nested level indentation */
.dt-menu-item[style*="--dt-menu-level"] {
padding-left: calc(0.75rem + var(--dt-menu-level, 0) * 1rem);
}
/* Classic brand: rounded, thinner borders */
[data-brand="classic"] .dt-menu-item {
border-top-width: 2px;
border-radius: var(--radius-sm);
}
[data-brand="classic"] .dt-menu-item:hover {
clip-path: none;
border-radius: var(--radius-sm);
}
[data-brand="classic"] .dt-menu-item.selected {
clip-path: none;
border-radius: var(--radius-sm);
}
/* ============================================================================
Filter Header — thick border accent bar
Source: dt-shopify-storefront FilterAccordion header
============================================================================ */
[data-brand="dt"] .dt-filter-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-top: 5px solid var(--color-primary);
border-bottom: 1px solid rgba(var(--color-primary-rgb), 0.2);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
cursor: pointer;
}
[data-brand="dt"] .dt-filter-header.active {
color: var(--color-secondary);
border-top-color: var(--color-secondary);
}
[data-brand="classic"] .dt-filter-header {
border-top-width: 1px;
}
/* ============================================================================
Mobile Filter Overlay
Source: dt-shopify-storefront MobileFilterMenu
============================================================================ */
.dt-filter-overlay {
position: fixed;
inset: 0;
z-index: 1000;
display: flex;
flex-direction: column;
}
.dt-filter-overlay-backdrop {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.dt-filter-overlay-content {
position: relative;
margin-top: auto;
max-height: 85vh;
background: var(--color-bg);
overflow-y: auto;
animation: dt-slide-up 0.3s ease-in-out both;
}

View File

@@ -35,7 +35,7 @@
Link Glow
============================================================================ */
[data-brand="dt"] a:hover {
text-shadow: 0 0 8px var(--color-primary);
text-shadow: 0 0 8px var(--dt-glow-color, var(--color-primary));
}
/* ============================================================================
@@ -49,8 +49,9 @@
/* ============================================================================
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));
[data-brand="dt"] .card:hover,
[data-brand="dt"] .dt-bevel-card:hover {
filter: drop-shadow(0 0 8px rgba(var(--dt-card-color-rgb, var(--color-primary-rgb)), 0.3));
}
/* ============================================================================

View File

@@ -0,0 +1,39 @@
/* dt-web-theme: Scrollbar Styling */
/* Source: dt-shopify-storefront cyberpunk scrollbar pattern */
[data-brand="dt"] .dt-scrollbar {
scrollbar-width: thin;
scrollbar-color: var(--color-primary) transparent;
}
[data-brand="dt"] .dt-scrollbar::-webkit-scrollbar {
width: 6px;
}
[data-brand="dt"] .dt-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
[data-brand="dt"] .dt-scrollbar::-webkit-scrollbar-thumb {
background: var(--color-primary);
border-radius: 3px;
}
/* Mode-aware scrollbar: uses local --dt-card-color if set */
[data-brand="dt"] .dt-scrollbar-mode {
scrollbar-width: thin;
scrollbar-color: var(--dt-card-color, var(--color-primary)) transparent;
}
[data-brand="dt"] .dt-scrollbar-mode::-webkit-scrollbar {
width: 6px;
}
[data-brand="dt"] .dt-scrollbar-mode::-webkit-scrollbar-track {
background: transparent;
}
[data-brand="dt"] .dt-scrollbar-mode::-webkit-scrollbar-thumb {
background: var(--dt-card-color, var(--color-primary));
border-radius: 3px;
}

View File

@@ -15,3 +15,6 @@
@import './components/bevels.css';
@import './components/glows.css';
@import './components/forms-dt.css';
@import './components/animations.css';
@import './components/scrollbar.css';
@import './components/feature-legend.css';