Remove glows, refine feature legend, forms, and showcase pages
- Delete glows.css and GlowsPage (unused) - Enhance DTFeatureLegend and feature-legend.css with improved layout - Update DTRadioGroup styling - Refine bevels.css, forms-dt.css - Update AnimationsPage, CardsAdvancedPage, FormsPage, HomePage - Add type:module to web package.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,20 +1,25 @@
|
|||||||
/**
|
/**
|
||||||
* DTFeatureLegend — Grid of product feature icons with rotated labels.
|
* DTFeatureLegend — Interactive grid of product feature icons with rotated labels.
|
||||||
*
|
*
|
||||||
* CSS reference: feature-legend.css .dt-feature-legend, .dt-feature-legend-header,
|
* Matches dt-shopify-storefront UseCaseLegend: header bar with hover details,
|
||||||
* .dt-feature-legend-grid, .dt-feature-legend-item, .dt-feature-supported, etc.
|
* rotated vertical labels (above for row 0, below for row 1), ? toggle,
|
||||||
|
* bordered grid with dark cell backgrounds, and pulse-on-hover icons.
|
||||||
|
*
|
||||||
|
* CSS reference: feature-legend.css
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ReactNode, CSSProperties } from 'react';
|
import { useState, type ReactNode, type CSSProperties } from 'react';
|
||||||
import type { DTVariant } from '@dangerousthings/tokens';
|
import type { DTVariant } from '@dangerousthings/tokens';
|
||||||
import { cx } from '../utils/cx';
|
import { cx } from '../utils/cx';
|
||||||
import { getVariantClass, featureStateToVariant } from '../utils/variantClasses';
|
import { getVariantClass } from '../utils/variantClasses';
|
||||||
|
|
||||||
export interface DTFeatureItem {
|
export interface DTFeatureItem {
|
||||||
key: string;
|
key: string;
|
||||||
name: string;
|
name: string;
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
state: 'supported' | 'disabled' | 'unsupported';
|
state: 'supported' | 'disabled' | 'unsupported';
|
||||||
|
/** Optional detail text shown in the header on hover */
|
||||||
|
detail?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateClassMap: Record<DTFeatureItem['state'], string> = {
|
const stateClassMap: Record<DTFeatureItem['state'], string> = {
|
||||||
@@ -32,6 +37,8 @@ interface DTFeatureLegendProps {
|
|||||||
columns?: number;
|
columns?: number;
|
||||||
/** Icon size in px @default 42 */
|
/** Icon size in px @default 42 */
|
||||||
iconSize?: number;
|
iconSize?: number;
|
||||||
|
/** Show rotated labels initially @default true */
|
||||||
|
showLabels?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
@@ -42,35 +49,93 @@ export function DTFeatureLegend({
|
|||||||
variant = 'normal',
|
variant = 'normal',
|
||||||
columns = 5,
|
columns = 5,
|
||||||
iconSize = 42,
|
iconSize = 42,
|
||||||
|
showLabels: initialShowLabels = true,
|
||||||
className,
|
className,
|
||||||
style,
|
style,
|
||||||
}: DTFeatureLegendProps) {
|
}: DTFeatureLegendProps) {
|
||||||
// Suppress unused import warning — featureStateToVariant is available for consumers
|
const [showLabels, setShowLabels] = useState(initialShowLabels);
|
||||||
void featureStateToVariant;
|
const [hoveredFeature, setHoveredFeature] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const hoveredItem = hoveredFeature
|
||||||
|
? features.find(f => f.key === hoveredFeature)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const rowCount = Math.ceil(features.length / columns);
|
||||||
|
const hasBottomRow = rowCount >= 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cx('dt-feature-legend', getVariantClass(variant), className)}
|
className={cx('dt-feature-legend', getVariantClass(variant), className)}
|
||||||
style={style}>
|
style={style}>
|
||||||
|
|
||||||
|
{/* Header bar — title + hovered feature detail + ? toggle */}
|
||||||
{title && (
|
{title && (
|
||||||
<div className="dt-feature-legend-header">{title}</div>
|
<div className="dt-feature-legend-header">
|
||||||
)}
|
<div className="dt-feature-legend-header-content">
|
||||||
<div
|
<div className="dt-feature-legend-title">{title}</div>
|
||||||
className="dt-feature-legend-grid"
|
<div className="dt-feature-legend-detail">
|
||||||
style={{ '--dt-feature-columns': columns } as CSSProperties}>
|
{hoveredItem ? (
|
||||||
{features.map(feature => (
|
<span className={stateClassMap[hoveredItem.state]}>
|
||||||
<div
|
{hoveredItem.name}{hoveredItem.detail ? ': ' : ''}
|
||||||
key={feature.key}
|
{hoveredItem.detail && <strong>{hoveredItem.detail}</strong>}
|
||||||
className={cx('dt-feature-legend-item', stateClassMap[feature.state])}
|
</span>
|
||||||
style={{ width: `${100 / columns}%` }}>
|
) : (
|
||||||
<div
|
<span className="dt-feature-legend-hint">
|
||||||
className="dt-feature-legend-icon"
|
Hover a feature for details
|
||||||
style={{ width: `${iconSize}px`, height: `${iconSize}px` }}>
|
</span>
|
||||||
{feature.icon}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="dt-feature-legend-label">{feature.name}</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<button
|
||||||
|
className="dt-feature-legend-toggle"
|
||||||
|
onClick={() => setShowLabels(v => !v)}
|
||||||
|
aria-label={showLabels ? 'Hide feature labels' : 'Show feature labels'}
|
||||||
|
title={showLabels ? 'Hide labels' : 'Show labels'}
|
||||||
|
type="button">
|
||||||
|
?
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Icon grid with border */}
|
||||||
|
<div className="dt-feature-legend-body">
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'dt-feature-legend-grid',
|
||||||
|
showLabels && 'with-labels',
|
||||||
|
showLabels && hasBottomRow && 'with-labels-bottom',
|
||||||
|
)}
|
||||||
|
style={{ '--dt-feature-columns': columns } as CSSProperties}>
|
||||||
|
{features.map((feature, index) => {
|
||||||
|
const row = Math.floor(index / columns);
|
||||||
|
const labelPosition = rowCount >= 2 ? (row === 0 ? 'above' : 'below') : 'above';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={feature.key}
|
||||||
|
className={cx('dt-feature-legend-item', stateClassMap[feature.state])}
|
||||||
|
style={{ width: `${100 / columns}%` }}
|
||||||
|
onMouseEnter={() => setHoveredFeature(feature.key)}
|
||||||
|
onMouseLeave={() => setHoveredFeature(null)}>
|
||||||
|
|
||||||
|
{/* Rotated vertical label — always in DOM, visibility via CSS */}
|
||||||
|
<div className={cx(
|
||||||
|
'dt-feature-legend-rotated-label',
|
||||||
|
labelPosition === 'below' ? 'label-below' : 'label-above',
|
||||||
|
showLabels && 'label-visible',
|
||||||
|
)}>
|
||||||
|
<span>{feature.name}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="dt-feature-legend-icon"
|
||||||
|
style={{ width: `${iconSize}px`, height: `${iconSize}px` }}>
|
||||||
|
{feature.icon}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* DTRadioGroup — Hexagonal radio buttons.
|
* DTRadioGroup — Hexagonal radio buttons.
|
||||||
*
|
*
|
||||||
* CSS reference: forms-dt.css input[type="radio"], .dt-radio-option
|
* Uses a custom span for the hexagon indicator since ::before pseudo-elements
|
||||||
|
* on <input> are unreliable in Chromium/Electron.
|
||||||
|
*
|
||||||
|
* CSS reference: forms-dt.css .dt-radio-hex, .dt-radio-option
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useId, type CSSProperties } from 'react';
|
import { useId, type CSSProperties } from 'react';
|
||||||
@@ -39,20 +42,27 @@ export function DTRadioGroup({
|
|||||||
className={cx('dt-radio-group', className)}
|
className={cx('dt-radio-group', className)}
|
||||||
style={{ display: 'flex', flexDirection: 'column', gap: '4px', ...style }}
|
style={{ display: 'flex', flexDirection: 'column', gap: '4px', ...style }}
|
||||||
role="radiogroup">
|
role="radiogroup">
|
||||||
{options.map(option => (
|
{options.map(option => {
|
||||||
<label
|
const checked = value === option.value;
|
||||||
key={option.value}
|
return (
|
||||||
className={cx('dt-radio-option', value === option.value && 'selected')}>
|
<label
|
||||||
<input
|
key={option.value}
|
||||||
type="radio"
|
className={cx('dt-radio-option', checked && 'selected')}>
|
||||||
name={groupName}
|
<input
|
||||||
value={option.value}
|
type="radio"
|
||||||
checked={value === option.value}
|
name={groupName}
|
||||||
onChange={() => onChange(option.value)}
|
value={option.value}
|
||||||
/>
|
checked={checked}
|
||||||
<span>{option.label}</span>
|
onChange={() => onChange(option.value)}
|
||||||
</label>
|
style={{ position: 'absolute', opacity: 0, width: 0, height: 0 }}
|
||||||
))}
|
/>
|
||||||
|
<span className={cx('dt-radio-hex', checked && 'checked')}>
|
||||||
|
<span className="dt-radio-hex-inner" />
|
||||||
|
</span>
|
||||||
|
<span>{option.label}</span>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { DTCard, DTStaggerContainer } from '@dangerousthings/react';
|
import { DTCard, DTStaggerContainer } from '@dangerousthings/react';
|
||||||
import { Section, Row, CodeLabel } from '../components/Section';
|
import { Section, Row, CodeLabel } from '../components/Section';
|
||||||
|
|
||||||
@@ -25,6 +25,69 @@ function AnimBox({ className, label }: { className: string; label: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VARIANTS = ['normal', 'emphasis', 'warning', 'success', 'other'] as const;
|
||||||
|
|
||||||
|
function ProgressBarDemo() {
|
||||||
|
const [progress, setProgress] = useState<number[]>([0, 0, 0, 0, 0]);
|
||||||
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
const [running, setRunning] = useState(false);
|
||||||
|
|
||||||
|
const animate = () => {
|
||||||
|
// Reset then animate up
|
||||||
|
setProgress([0, 0, 0, 0, 0]);
|
||||||
|
setRunning(true);
|
||||||
|
let tick = 0;
|
||||||
|
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||||
|
intervalRef.current = setInterval(() => {
|
||||||
|
tick++;
|
||||||
|
setProgress(prev =>
|
||||||
|
prev.map((_, i) => Math.min(100, Math.round(tick * (1.5 + i * 0.4))))
|
||||||
|
);
|
||||||
|
if (tick > 80) {
|
||||||
|
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||||
|
setRunning(false);
|
||||||
|
}
|
||||||
|
}, 40);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Auto-run on mount
|
||||||
|
const t = setTimeout(animate, 500);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(t);
|
||||||
|
if (intervalRef.current) clearInterval(intervalRef.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', marginBottom: 16 }}>
|
||||||
|
{VARIANTS.map((v, i) => (
|
||||||
|
<DTCard
|
||||||
|
key={v}
|
||||||
|
variant={v}
|
||||||
|
title={`${v.toUpperCase()} ${progress[i]}%`}
|
||||||
|
progress={progress[i]}
|
||||||
|
style={{ width: 160, transition: 'all 0.1s ease-out' }}
|
||||||
|
>
|
||||||
|
<div className="card-body" style={{ fontSize: '0.75rem', minHeight: 60, display: 'flex', alignItems: 'center' }}>
|
||||||
|
Progress fills the left edge from bottom to top
|
||||||
|
</div>
|
||||||
|
</DTCard>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="btn-secondary"
|
||||||
|
onClick={animate}
|
||||||
|
disabled={running}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{running ? 'ANIMATING...' : 'REPLAY PROGRESS'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function AnimationsPage() {
|
export function AnimationsPage() {
|
||||||
const [entranceKey, setEntranceKey] = useState(0);
|
const [entranceKey, setEntranceKey] = useState(0);
|
||||||
const [staggerKey, setStaggerKey] = useState(0);
|
const [staggerKey, setStaggerKey] = useState(0);
|
||||||
@@ -126,6 +189,11 @@ export function AnimationsPage() {
|
|||||||
<CodeLabel text=".dt-transition-accordion | .dt-transition-chevron | .dt-transition-progress" />
|
<CodeLabel text=".dt-transition-accordion | .dt-transition-chevron | .dt-transition-progress" />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
|
<Section title="Card Progress Bar" description="Cards have a vertical progress bar on the left edge. Animate it by transitioning --dt-card-progress from 0 to 100.">
|
||||||
|
<ProgressBarDemo />
|
||||||
|
<CodeLabel text="<DTCard progress={value} /> — CSS ::after gradient driven by --dt-card-progress" />
|
||||||
|
</Section>
|
||||||
|
|
||||||
<Section title="Scrollbar Styling" description="Thin neon scrollbar scoped under [data-brand='dt'].">
|
<Section title="Scrollbar Styling" description="Thin neon scrollbar scoped under [data-brand='dt'].">
|
||||||
<div
|
<div
|
||||||
className="dt-scrollbar"
|
className="dt-scrollbar"
|
||||||
|
|||||||
@@ -33,30 +33,31 @@ const modes: DTVariant[] = ['normal', 'emphasis', 'warning', 'success', 'other']
|
|||||||
const ico = (C: any) => createElement(C, { style: { fontSize: '2rem' } });
|
const ico = (C: any) => createElement(C, { style: { fontSize: '2rem' } });
|
||||||
|
|
||||||
// Full chip feature legend (9 features — from storefront UseCaseLegend)
|
// Full chip feature legend (9 features — from storefront UseCaseLegend)
|
||||||
|
// detail text simulates what the storefront shows for an NExT implant
|
||||||
const chipFeatures: DTFeatureItem[] = [
|
const chipFeatures: DTFeatureItem[] = [
|
||||||
{ key: 'smartphone', name: 'Smartphone', icon: ico(MdOutlinePhonelinkRing), state: 'supported' },
|
{ key: 'smartphone', name: 'Smartphone', icon: ico(MdOutlinePhonelinkRing), state: 'supported', detail: 'Full NFC smartphone support' },
|
||||||
{ key: 'access_control', name: 'Access Control', icon: ico(MdOutlineVpnKey), state: 'supported' },
|
{ key: 'access_control', name: 'Access Control', icon: ico(MdOutlineVpnKey), state: 'supported', detail: 'DESFire, MIFARE Classic, iCLASS' },
|
||||||
{ key: 'digital_security', name: 'Digital Security', icon: ico(FaUserShield), state: 'supported' },
|
{ key: 'digital_security', name: 'Digital Security', icon: ico(FaUserShield), state: 'supported', detail: 'FIDO2 / WebAuthn' },
|
||||||
{ key: 'cryptography', name: 'Cryptography', icon: ico(LuBinary), state: 'unsupported' },
|
{ key: 'cryptography', name: 'Cryptography', icon: ico(LuBinary), state: 'unsupported', detail: 'Not Supported' },
|
||||||
{ key: 'data_sharing', name: 'Data Sharing', icon: ico(MdOutlineMobileScreenShare), state: 'supported' },
|
{ key: 'data_sharing', name: 'Data Sharing', icon: ico(MdOutlineMobileScreenShare), state: 'supported', detail: 'NDEF records, vCard, URL' },
|
||||||
{ key: 'payment', name: 'Payment', icon: ico(MdOutlineCreditCard), state: 'disabled' },
|
{ key: 'payment', name: 'Payment', icon: ico(MdOutlineCreditCard), state: 'disabled', detail: 'Apex required' },
|
||||||
{ key: 'magic', name: 'UID Magic', icon: ico(MdOutlineCopyAll), state: 'supported' },
|
{ key: 'magic', name: 'Magic', icon: ico(MdOutlineCopyAll), state: 'supported', detail: 'Gen2 Magic UID' },
|
||||||
{ key: 'illumination', name: 'Illumination', icon: ico(MdOutlineLightbulb), state: 'unsupported' },
|
{ key: 'Illumination', name: 'Illumination', icon: ico(MdOutlineLightbulb), state: 'unsupported', detail: 'None' },
|
||||||
{ key: 'temperature', name: 'Temperature', icon: ico(MdOutlineThermostat), state: 'unsupported' },
|
{ key: 'temperature', name: 'Sensors', icon: ico(MdOutlineThermostat), state: 'unsupported', detail: 'None' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Full biomagnet feature legend (4 features — from storefront MagnetUseCaseLegend)
|
// Full biomagnet feature legend (4 features — from storefront MagnetUseCaseLegend)
|
||||||
const magnetFeatures: DTFeatureItem[] = [
|
const magnetFeatures: DTFeatureItem[] = [
|
||||||
{ key: 'sensing', name: 'Sensing', icon: ico(MdOutlineSensors), state: 'supported' },
|
{ key: 'sensing', name: 'Sensing', icon: ico(MdOutlineSensors), state: 'supported', detail: 'Electromagnetic field detection' },
|
||||||
{ key: 'lifting', name: 'Lifting', icon: ico(MdOutlineFitbit), state: 'supported' },
|
{ key: 'lifting', name: 'Lifting', icon: ico(MdOutlineFitbit), state: 'supported', detail: '2.1 kg lifting force' },
|
||||||
{ key: 'haptics', name: 'Haptics', icon: ico(MdOutlineVibration), state: 'supported' },
|
{ key: 'haptics', name: 'Haptics', icon: ico(MdOutlineVibration), state: 'supported', detail: 'Tactile vibration feedback' },
|
||||||
{ key: 'polarity', name: 'Polarity Detection', icon: ico(MdOutlineExplore), state: 'unsupported' },
|
{ key: 'polarity', name: 'Polarity Detection', icon: ico(MdOutlineExplore), state: 'unsupported', detail: 'Not Supported' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Full aesthetic feature legend (2 features — from storefront AestheticUseCaseLegend)
|
// Full aesthetic feature legend (2 features — from storefront AestheticUseCaseLegend)
|
||||||
const aestheticFeatures: DTFeatureItem[] = [
|
const aestheticFeatures: DTFeatureItem[] = [
|
||||||
{ key: 'illumination', name: 'Illumination', icon: ico(MdLightbulbOutline), state: 'supported' },
|
{ key: 'illumination', name: 'Illumination', icon: ico(MdLightbulbOutline), state: 'supported', detail: 'LED: Red, Green, Blue, White' },
|
||||||
{ key: 'prominence', name: 'Prominence', icon: ico(MdOutlineVisibility), state: 'supported' },
|
{ key: 'prominence', name: 'Prominence', icon: ico(MdOutlineVisibility), state: 'supported', detail: 'High visibility under skin' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function CardsAdvancedPage() {
|
export function CardsAdvancedPage() {
|
||||||
@@ -108,30 +109,36 @@ export function CardsAdvancedPage() {
|
|||||||
<CodeLabel text=".dt-badge-parent > .dt-card-badge — badge positioned on card image container" />
|
<CodeLabel text=".dt-badge-parent > .dt-card-badge — badge positioned on card image container" />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Buttons" description="Beveled buttons with active/selected states, mode colors, and level indentation.">
|
<Section title="Buttons" description="Beveled buttons with active/selected states and mode colors. Nested levels use inline paddingLeft.">
|
||||||
<div style={{ maxWidth: 300 }}>
|
<div style={{ maxWidth: 300 }}>
|
||||||
{['All Products', 'NFC Implants', 'RFID Tags', 'Accessories', 'Lab Products'].map((item, i) => (
|
{[
|
||||||
|
{ name: 'All Products', cls: ' active', pad: 0 },
|
||||||
|
{ name: 'NFC Implants', cls: '', pad: 0 },
|
||||||
|
{ name: 'RFID Tags', cls: '', pad: 32 },
|
||||||
|
{ name: 'Accessories', cls: '', pad: 32 },
|
||||||
|
{ name: 'Lab Products', cls: ' mode-warning', pad: 0 },
|
||||||
|
].map(item => (
|
||||||
<button
|
<button
|
||||||
key={item}
|
key={item.name}
|
||||||
className={`dt-menu-item${i === 0 ? ' active' : ''}${i === 4 ? ' mode-warning' : ''}`}
|
className={`dt-menu-item${item.cls}`}
|
||||||
style={i > 1 && i < 4 ? { '--dt-menu-level': '1' } as CSSProperties : undefined}
|
style={item.pad ? { paddingLeft: item.pad } : undefined}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{item}
|
{item.name}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<CodeLabel text=".dt-menu-item | .dt-menu-item.active | --dt-menu-level: 1" />
|
<CodeLabel text=".dt-menu-item | .active | paddingLeft for nesting" />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Feature Legends" description="Product feature grids from the storefront. Icons indicate feature capabilities; color indicates state.">
|
<Section title="Feature Legends" description="Interactive product feature grids from the storefront. Hover icons for details. Toggle labels with the ? button.">
|
||||||
<DTFeatureLegend features={chipFeatures} title="NFC CHIP FEATURES" variant="normal" columns={5} />
|
<DTFeatureLegend features={chipFeatures} title="NExT Features" variant="normal" columns={5} />
|
||||||
<div style={{ height: 'var(--space-6)' }} />
|
<div style={{ height: 'var(--space-6)' }} />
|
||||||
<DTFeatureLegend features={magnetFeatures} title="BIOMAGNET FEATURES" variant="emphasis" columns={4} />
|
<DTFeatureLegend features={magnetFeatures} title="Titan Features" variant="emphasis" columns={4} />
|
||||||
<div style={{ height: 'var(--space-6)' }} />
|
<div style={{ height: 'var(--space-6)' }} />
|
||||||
<DTFeatureLegend features={aestheticFeatures} title="AESTHETIC FEATURES" variant="other" columns={2} />
|
<DTFeatureLegend features={aestheticFeatures} title="xLED Features" variant="other" columns={2} />
|
||||||
<div style={{ height: 'var(--space-4)' }} />
|
<div style={{ height: 'var(--space-4)' }} />
|
||||||
<CodeLabel text="<DTFeatureLegend features={chipFeatures} title='NFC CHIP FEATURES' variant='normal' />" />
|
<CodeLabel text="<DTFeatureLegend features={chipFeatures} title='NExT Features' variant='normal' />" />
|
||||||
<CodeLabel text="<DTFeatureLegend features={magnetFeatures} variant='emphasis' columns={4} />" />
|
<CodeLabel text="<DTFeatureLegend features={magnetFeatures} variant='emphasis' columns={4} />" />
|
||||||
<CodeLabel text="<DTFeatureLegend features={aestheticFeatures} variant='other' columns={2} />" />
|
<CodeLabel text="<DTFeatureLegend features={aestheticFeatures} variant='other' columns={2} />" />
|
||||||
<div style={{ marginTop: 'var(--space-3)', display: 'flex', gap: 'var(--space-4)', fontSize: '0.75rem' }}>
|
<div style={{ marginTop: 'var(--space-3)', display: 'flex', gap: 'var(--space-4)', fontSize: '0.75rem' }}>
|
||||||
|
|||||||
@@ -102,20 +102,26 @@ export function FormsPage() {
|
|||||||
<CodeLabel text=".dt-filter-header | .dt-filter-header.active" />
|
<CodeLabel text=".dt-filter-header | .dt-filter-header.active" />
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section title="Buttons" description="Beveled buttons with active state, mode colors, and level indentation.">
|
<Section title="Buttons" description="Beveled buttons with active state and mode colors. Nested levels use inline paddingLeft.">
|
||||||
<div style={{ maxWidth: 300 }}>
|
<div style={{ maxWidth: 300 }}>
|
||||||
{['All Products', 'NFC Implants', 'RFID Tags', 'Accessories', 'Lab Products'].map((name, i) => (
|
{[
|
||||||
|
{ name: 'All Products', cls: ' active', pad: 0 },
|
||||||
|
{ name: 'NFC Implants', cls: '', pad: 0 },
|
||||||
|
{ name: 'RFID Tags', cls: '', pad: 32 },
|
||||||
|
{ name: 'Accessories', cls: '', pad: 32 },
|
||||||
|
{ name: 'Lab Products', cls: ' mode-warning', pad: 0 },
|
||||||
|
].map(item => (
|
||||||
<button
|
<button
|
||||||
key={name}
|
key={item.name}
|
||||||
className={`dt-menu-item${i === 0 ? ' active' : ''}${i === 4 ? ' mode-warning' : ''}`}
|
className={`dt-menu-item${item.cls}`}
|
||||||
style={(i === 2 || i === 3) ? { '--dt-menu-level': '1' } as React.CSSProperties : undefined}
|
style={item.pad ? { paddingLeft: item.pad } : undefined}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{name}
|
{item.name}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<CodeLabel text=".dt-menu-item | .active | --dt-menu-level: 1" />
|
<CodeLabel text=".dt-menu-item | .active | paddingLeft for nesting" />
|
||||||
</Section>
|
</Section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
import { DTCard } from '@dangerousthings/react';
|
|
||||||
import { Section, Row, CodeLabel } from '../components/Section';
|
|
||||||
|
|
||||||
export function GlowsPage() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1 className="page-title">Glows</h1>
|
|
||||||
<p className="page-subtitle">Neon drop-shadow and text-shadow effects. Active on the DT brand.</p>
|
|
||||||
|
|
||||||
<Section title="Button Glows" description="filter: drop-shadow() follows clip-path shape. Hover for enhanced glow.">
|
|
||||||
<Row>
|
|
||||||
<button className="btn-primary" type="button">PRIMARY GLOW</button>
|
|
||||||
<button className="btn-danger" type="button">DANGER GLOW</button>
|
|
||||||
</Row>
|
|
||||||
<CodeLabel text={'.btn-primary / .btn-danger — automatic on [data-brand="dt"]'} />
|
|
||||||
<br />
|
|
||||||
<Row>
|
|
||||||
<button className="btn-secondary" type="button">SECONDARY (HOVER)</button>
|
|
||||||
</Row>
|
|
||||||
<CodeLabel text=".btn-secondary — glow on hover" />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Link Glow" description="text-shadow on hover. Respects --dt-glow-color when set by a parent mode.">
|
|
||||||
<Row>
|
|
||||||
<a href="#" style={{ color: 'var(--color-primary)', fontWeight: 600, fontSize: '1.125rem' }}>
|
|
||||||
Hover this link for text glow
|
|
||||||
</a>
|
|
||||||
</Row>
|
|
||||||
<CodeLabel text="a:hover — uses var(--dt-glow-color, var(--color-primary))" />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Mode-Aware Link Glow" description="Links inside mode containers glow with that mode's color.">
|
|
||||||
<Row>
|
|
||||||
{(['normal', 'emphasis', 'warning', 'success', 'other'] as const).map(mode => (
|
|
||||||
<div key={mode} className={`mode-${mode}`} style={{ padding: 'var(--space-3)' }}>
|
|
||||||
<a href="#" style={{ fontWeight: 600, fontSize: '1rem' }}>{mode.toUpperCase()}</a>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
<CodeLabel text=".mode-emphasis a:hover — glows yellow via --dt-glow-color" />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Terminal Inset Glow" description="Inset + outer box-shadow. No clip-path so box-shadow works directly.">
|
|
||||||
<div className="terminal dt-accent-top">
|
|
||||||
<code>{'$ dt-web-theme --install\n> Installing design tokens...\n> Applying cyberpunk aesthetic...\n> Done. Welcome to the future.'}</code>
|
|
||||||
</div>
|
|
||||||
<CodeLabel text={'.terminal — automatic on [data-brand="dt"]'} />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Card Hover Glow" description="filter: drop-shadow() respects the beveled clip-path.">
|
|
||||||
<DTCard title="HOVER ME" style={{ cursor: 'pointer' }}>
|
|
||||||
<div className="card-body">Cards get a drop-shadow glow on hover.</div>
|
|
||||||
</DTCard>
|
|
||||||
<CodeLabel text={'.card:hover — automatic on [data-brand="dt"]'} />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Input Focus Glow" description="box-shadow: 0 4px 0 1px — bright bar beneath the input.">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="input"
|
|
||||||
placeholder="Click to focus — see the glow bar"
|
|
||||||
style={{ maxWidth: 400 }}
|
|
||||||
/>
|
|
||||||
<CodeLabel text={'.input:focus — automatic on [data-brand="dt"]'} />
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Glow Utilities" description="Generic utility classes for applying glow effects to any element.">
|
|
||||||
<Row>
|
|
||||||
<div className="dt-glow" style={{ background: 'var(--color-primary)', padding: 'var(--space-4) var(--space-6)', display: 'inline-block', fontWeight: 700, color: 'var(--color-bg)' }}>.dt-glow</div>
|
|
||||||
<div className="dt-glow-strong" style={{ background: 'var(--color-primary)', padding: 'var(--space-4) var(--space-6)', display: 'inline-block', fontWeight: 700, color: 'var(--color-bg)' }}>.dt-glow-strong</div>
|
|
||||||
</Row>
|
|
||||||
<CodeLabel text=".dt-glow | .dt-glow-strong" />
|
|
||||||
<Row>
|
|
||||||
<div className="dt-glow-inset" style={{ padding: 'var(--space-4) var(--space-6)', display: 'inline-block', fontWeight: 700, border: '1px solid var(--color-border)' }}>.dt-glow-inset</div>
|
|
||||||
<div className="dt-text-glow" style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-primary)', display: 'inline-block' }}>.dt-text-glow</div>
|
|
||||||
</Row>
|
|
||||||
<CodeLabel text=".dt-glow-inset | .dt-text-glow" />
|
|
||||||
</Section>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import { Section, CodeLabel } from '../components/Section';
|
|||||||
|
|
||||||
const categories: { hash: string; title: string; desc: string; mode: DTVariant; count: number }[] = [
|
const categories: { hash: string; title: string; desc: string; mode: DTVariant; count: number }[] = [
|
||||||
{ hash: 'bevels', title: 'Bevels', desc: 'Angular clip-path patterns — cards, buttons, badges, media frames, modals, drawers', mode: 'normal', count: 8 },
|
{ hash: 'bevels', title: 'Bevels', desc: 'Angular clip-path patterns — cards, buttons, badges, media frames, modals, drawers', mode: 'normal', count: 8 },
|
||||||
{ hash: 'glows', title: 'Glows', desc: 'Neon drop-shadow and text-shadow effects for the DT brand', mode: 'emphasis', count: 6 },
|
|
||||||
{ hash: 'forms', title: 'Forms', desc: 'Text inputs, checkboxes, switches, radios, progress bars, accordions, steppers', mode: 'success', count: 7 },
|
{ hash: 'forms', title: 'Forms', desc: 'Text inputs, checkboxes, switches, radios, progress bars, accordions, steppers', mode: 'success', count: 7 },
|
||||||
{ hash: 'animations', title: 'Animations', desc: 'Entrance animations, stagger containers, transition utilities, scrollbar styling', mode: 'other', count: 5 },
|
{ hash: 'animations', title: 'Animations', desc: 'Entrance animations, stagger containers, transition utilities, scrollbar styling', mode: 'other', count: 5 },
|
||||||
{ hash: 'cards-advanced', title: 'Advanced Cards', desc: 'Card color modes, progress bars, badge overlays, interactive bevel buttons, feature legend', mode: 'warning', count: 6 },
|
{ hash: 'cards-advanced', title: 'Advanced Cards', desc: 'Card color modes, progress bars, badge overlays, interactive bevel buttons, feature legend', mode: 'warning', count: 6 },
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"main": "dist/index.css",
|
"main": "dist/index.css",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./dist/index.css",
|
".": "./dist/index.css",
|
||||||
|
"./dist/index.css": "./dist/index.css",
|
||||||
"./tokens/*": "./dist/tokens/*",
|
"./tokens/*": "./dist/tokens/*",
|
||||||
"./components/*": "./dist/components/*",
|
"./components/*": "./dist/components/*",
|
||||||
"./fonts/*": "./dist/fonts/*",
|
"./fonts/*": "./dist/fonts/*",
|
||||||
|
|||||||
@@ -450,14 +450,13 @@
|
|||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
Card Color Modes — per-instance color overrides
|
Card Color Modes — per-instance color overrides
|
||||||
Sets --dt-card-color, --dt-card-color-selected, --dt-card-color-rgb,
|
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.
|
and --accent-mode for use by all card sub-components.
|
||||||
Source: dt-shopify-storefront .mode-* classes
|
Source: dt-shopify-storefront .mode-* classes
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
[data-brand="dt"] .mode-normal {
|
[data-brand="dt"] .mode-normal {
|
||||||
--dt-card-color: var(--mode-normal);
|
--dt-card-color: var(--mode-normal);
|
||||||
--dt-card-color-rgb: var(--mode-normal-rgb);
|
--dt-card-color-rgb: var(--mode-normal-rgb);
|
||||||
--dt-card-color-selected: var(--mode-normal-selected);
|
--dt-card-color-selected: var(--mode-normal-selected);
|
||||||
--dt-glow-color: var(--mode-normal);
|
|
||||||
--accent-mode: var(--mode-emphasis);
|
--accent-mode: var(--mode-emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +464,6 @@
|
|||||||
--dt-card-color: var(--mode-emphasis);
|
--dt-card-color: var(--mode-emphasis);
|
||||||
--dt-card-color-rgb: var(--mode-emphasis-rgb);
|
--dt-card-color-rgb: var(--mode-emphasis-rgb);
|
||||||
--dt-card-color-selected: var(--mode-emphasis-selected);
|
--dt-card-color-selected: var(--mode-emphasis-selected);
|
||||||
--dt-glow-color: var(--mode-emphasis);
|
|
||||||
--accent-mode: var(--mode-normal);
|
--accent-mode: var(--mode-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,7 +471,6 @@
|
|||||||
--dt-card-color: var(--mode-warning);
|
--dt-card-color: var(--mode-warning);
|
||||||
--dt-card-color-rgb: var(--mode-warning-rgb);
|
--dt-card-color-rgb: var(--mode-warning-rgb);
|
||||||
--dt-card-color-selected: var(--mode-warning-selected);
|
--dt-card-color-selected: var(--mode-warning-selected);
|
||||||
--dt-glow-color: var(--mode-warning);
|
|
||||||
--accent-mode: var(--mode-emphasis);
|
--accent-mode: var(--mode-emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +478,6 @@
|
|||||||
--dt-card-color: var(--mode-success);
|
--dt-card-color: var(--mode-success);
|
||||||
--dt-card-color-rgb: var(--mode-success-rgb);
|
--dt-card-color-rgb: var(--mode-success-rgb);
|
||||||
--dt-card-color-selected: var(--mode-success-selected);
|
--dt-card-color-selected: var(--mode-success-selected);
|
||||||
--dt-glow-color: var(--mode-success);
|
|
||||||
--accent-mode: var(--mode-emphasis);
|
--accent-mode: var(--mode-emphasis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +485,6 @@
|
|||||||
--dt-card-color: var(--mode-other);
|
--dt-card-color: var(--mode-other);
|
||||||
--dt-card-color-rgb: var(--mode-other-rgb);
|
--dt-card-color-rgb: var(--mode-other-rgb);
|
||||||
--dt-card-color-selected: var(--mode-other-selected);
|
--dt-card-color-selected: var(--mode-other-selected);
|
||||||
--dt-glow-color: var(--mode-other);
|
|
||||||
--accent-mode: var(--mode-normal);
|
--accent-mode: var(--mode-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,55 +2,184 @@
|
|||||||
/* Source: dt-shopify-storefront UseCaseLegend component */
|
/* Source: dt-shopify-storefront UseCaseLegend component */
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
Feature Legend — icon grid with rotated labels
|
Feature Legend — interactive icon grid with rotated labels, hover details,
|
||||||
|
? toggle, bordered grid with dark cell backgrounds.
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
.dt-feature-legend {
|
.dt-feature-legend {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Header bar ---- */
|
||||||
.dt-feature-legend-header {
|
.dt-feature-legend-header {
|
||||||
padding: 8px 16px;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
background: var(--dt-card-color, var(--color-primary));
|
background: var(--dt-card-color, var(--color-primary));
|
||||||
color: var(--color-bg);
|
color: var(--color-bg);
|
||||||
|
transition: height 0.2s ease-out, opacity 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-header-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 8px 12px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-title {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
line-height: 1.1;
|
||||||
|
color: var(--color-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-detail {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
text-transform: uppercase;
|
margin-top: 2px;
|
||||||
letter-spacing: 0.05em;
|
min-height: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-detail .dt-feature-supported { color: var(--color-bg); }
|
||||||
|
.dt-feature-legend-detail .dt-feature-disabled { color: var(--color-bg); opacity: 0.7; }
|
||||||
|
.dt-feature-legend-detail .dt-feature-unsupported { color: #7f1d1d; }
|
||||||
|
|
||||||
|
.dt-feature-legend-hint {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ? toggle button on right edge */
|
||||||
|
.dt-feature-legend-toggle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 30px;
|
||||||
|
background: var(--dt-card-color, var(--color-primary));
|
||||||
|
color: var(--color-bg);
|
||||||
|
border: none;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-toggle:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Grid body ---- */
|
||||||
|
.dt-feature-legend-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dt-feature-legend-grid {
|
.dt-feature-legend-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0;
|
justify-content: space-around;
|
||||||
|
flex: 1;
|
||||||
|
border: 2px solid var(--dt-card-color, var(--color-primary));
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
overflow: visible;
|
||||||
|
transition: padding 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-grid.with-labels {
|
||||||
|
padding-top: 160px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-grid.with-labels.with-labels-bottom {
|
||||||
|
padding-bottom: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Grid item / cell ---- */
|
||||||
.dt-feature-legend-item {
|
.dt-feature-legend-item {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
justify-content: center;
|
||||||
padding: 8px 4px;
|
padding: 8px 4px;
|
||||||
flex: 0 0 20%; /* 5 columns */
|
background: var(--color-bg);
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Icon ---- */
|
||||||
.dt-feature-legend-icon {
|
.dt-feature-legend-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: var(--dt-feature-color, var(--color-primary));
|
color: var(--dt-feature-color, var(--color-primary));
|
||||||
|
transition: opacity 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dt-feature-legend-label {
|
.dt-feature-legend-item:hover .dt-feature-legend-icon {
|
||||||
font-size: 0.75rem;
|
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Rotated vertical label ---- */
|
||||||
|
.dt-feature-legend-rotated-label {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px 2px;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease-out 0.15s; /* delay so padding opens first */
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-rotated-label.label-visible {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-rotated-label.label-above {
|
||||||
|
bottom: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-rotated-label.label-below {
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dt-feature-legend-rotated-label span {
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
color: var(--dt-feature-color, var(--color-primary));
|
||||||
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--dt-feature-color, var(--color-text-primary));
|
white-space: nowrap;
|
||||||
text-align: center;
|
padding: 4px 2px;
|
||||||
line-height: 1.2;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Feature state color classes */
|
/* ---- Feature state color classes ---- */
|
||||||
.dt-feature-supported { --dt-feature-color: var(--mode-normal, var(--color-primary)); }
|
.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-disabled { --dt-feature-color: var(--mode-emphasis, var(--color-secondary)); }
|
||||||
.dt-feature-unsupported { --dt-feature-color: var(--mode-warning, var(--color-error)); }
|
.dt-feature-unsupported { --dt-feature-color: var(--mode-warning, var(--color-error)); }
|
||||||
|
|
||||||
|
/* ---- Variant header colors ---- */
|
||||||
|
.mode-normal .dt-feature-legend-header,
|
||||||
|
.mode-normal .dt-feature-legend-toggle { background: var(--mode-normal, var(--color-primary)); }
|
||||||
|
.mode-normal .dt-feature-legend-grid { border-color: var(--mode-normal, var(--color-primary)); }
|
||||||
|
|
||||||
|
.mode-emphasis .dt-feature-legend-header,
|
||||||
|
.mode-emphasis .dt-feature-legend-toggle { background: var(--mode-emphasis, var(--color-secondary)); }
|
||||||
|
.mode-emphasis .dt-feature-legend-grid { border-color: var(--mode-emphasis, var(--color-secondary)); }
|
||||||
|
|
||||||
|
.mode-warning .dt-feature-legend-header,
|
||||||
|
.mode-warning .dt-feature-legend-toggle { background: var(--mode-warning, var(--color-error)); }
|
||||||
|
.mode-warning .dt-feature-legend-grid { border-color: var(--mode-warning, var(--color-error)); }
|
||||||
|
|
||||||
|
.mode-success .dt-feature-legend-header,
|
||||||
|
.mode-success .dt-feature-legend-toggle { background: var(--mode-success, var(--color-accent)); }
|
||||||
|
.mode-success .dt-feature-legend-grid { border-color: var(--mode-success, var(--color-accent)); }
|
||||||
|
|
||||||
|
.mode-other .dt-feature-legend-header,
|
||||||
|
.mode-other .dt-feature-legend-toggle { background: var(--mode-other, var(--color-other)); }
|
||||||
|
.mode-other .dt-feature-legend-grid { border-color: var(--mode-other, var(--color-other)); }
|
||||||
|
|||||||
@@ -143,49 +143,38 @@
|
|||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
Radio Button — Hexagonal indicator
|
Radio Button — Hexagonal indicator
|
||||||
Matches RN DTRadioGroup: outlined hexagon with inner filled dot on check.
|
Matches RN DTRadioGroup: outlined hexagon with inner filled dot on check.
|
||||||
Outer hexagon is always stroked (accent border); inner dot appears on check.
|
Uses a custom span (.dt-radio-hex) since ::before on <input> is unreliable.
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
[data-brand="dt"] input[type="radio"] {
|
[data-brand="dt"] .dt-radio-hex {
|
||||||
appearance: none;
|
position: relative;
|
||||||
-webkit-appearance: none;
|
display: inline-block;
|
||||||
display: grid;
|
|
||||||
place-content: center;
|
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
cursor: pointer;
|
|
||||||
clip-path: polygon(
|
clip-path: polygon(
|
||||||
50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%
|
50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%
|
||||||
);
|
);
|
||||||
transition: background var(--transition-fast);
|
transition: background var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inner surface — creates the outlined hexagon look (accent border + transparent center) */
|
/* Inner surface — outlined hexagon look (accent border + dark center) */
|
||||||
[data-brand="dt"] input[type="radio"]::before {
|
[data-brand="dt"] .dt-radio-hex-inner {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 2px;
|
inset: 2px;
|
||||||
background: var(--color-bg);
|
background: var(--color-bg);
|
||||||
clip-path: polygon(
|
clip-path: polygon(
|
||||||
50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%
|
50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%
|
||||||
);
|
);
|
||||||
transition: background var(--transition-fast);
|
transition: all var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checked: inner dot fills with accent color (smaller inset for the dot effect) */
|
/* Checked: inner dot fills with accent color (smaller inset) */
|
||||||
[data-brand="dt"] input[type="radio"]:checked::before {
|
[data-brand="dt"] .dt-radio-hex.checked .dt-radio-hex-inner {
|
||||||
inset: 5px;
|
inset: 5px;
|
||||||
background: var(--color-primary);
|
background: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-brand="dt"] input[type="radio"]:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Radio group option container */
|
/* Radio group option container */
|
||||||
.dt-radio-option {
|
.dt-radio-option {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -403,11 +392,6 @@
|
|||||||
animation: dt-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
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 */
|
/* Classic brand: rounded, thinner borders */
|
||||||
[data-brand="classic"] .dt-menu-item {
|
[data-brand="classic"] .dt-menu-item {
|
||||||
border-top-width: 2px;
|
border-top-width: 2px;
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
/* 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(--dt-glow-color, 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,
|
|
||||||
[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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================================================
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
/* Component styles — DT visual identity */
|
/* Component styles — DT visual identity */
|
||||||
@import './components/bevels.css';
|
@import './components/bevels.css';
|
||||||
@import './components/glows.css';
|
|
||||||
@import './components/forms-dt.css';
|
@import './components/forms-dt.css';
|
||||||
@import './components/animations.css';
|
@import './components/animations.css';
|
||||||
@import './components/scrollbar.css';
|
@import './components/scrollbar.css';
|
||||||
|
|||||||
Reference in New Issue
Block a user