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>
110 lines
3.0 KiB
TypeScript
110 lines
3.0 KiB
TypeScript
/**
|
|
* DTHexagon — Decorative SVG hexagon with optional animations.
|
|
*
|
|
* Uses inline SVG (not CSS classes) — same approach as the RN version.
|
|
* Animation classes from animations.css applied to wrapper div.
|
|
*/
|
|
|
|
import type { ReactNode, CSSProperties } from 'react';
|
|
import type { DTVariant } from '@dangerousthings/tokens';
|
|
import { cx } from '../utils/cx';
|
|
import { getVariantClass } from '../utils/variantClasses';
|
|
|
|
interface DTHexagonProps {
|
|
/** Diameter in pixels */
|
|
size: number;
|
|
/** Color variant @default 'normal' */
|
|
variant?: DTVariant;
|
|
/** Filled or outline @default true */
|
|
filled?: boolean;
|
|
/** Enable animation @default false */
|
|
animated?: boolean;
|
|
/** Animation type @default 'rotate' */
|
|
animationType?: 'rotate' | 'pulse' | 'none';
|
|
/** Animation duration in ms @default 2000 */
|
|
animationDuration?: number;
|
|
/** Outline stroke width @default 2 */
|
|
borderWidth?: number;
|
|
/** Custom color override */
|
|
color?: string;
|
|
/** Opacity @default 1 */
|
|
opacity?: number;
|
|
/** Centered content inside hexagon */
|
|
children?: ReactNode;
|
|
className?: string;
|
|
style?: CSSProperties;
|
|
}
|
|
|
|
/** Flat-top hexagon points for given size */
|
|
function hexPoints(size: number): string {
|
|
const r = size / 2;
|
|
const cx = r;
|
|
const cy = r;
|
|
const points: [number, number][] = [];
|
|
for (let i = 0; i < 6; i++) {
|
|
const angle = (Math.PI / 3) * i - Math.PI / 6;
|
|
points.push([cx + r * Math.cos(angle), cy + r * Math.sin(angle)]);
|
|
}
|
|
return points.map(([x, y]) => `${x},${y}`).join(' ');
|
|
}
|
|
|
|
export function DTHexagon({
|
|
size,
|
|
variant = 'normal',
|
|
filled = true,
|
|
animated = false,
|
|
animationType = 'rotate',
|
|
animationDuration = 2000,
|
|
borderWidth = 2,
|
|
color,
|
|
opacity = 1,
|
|
children,
|
|
className,
|
|
style,
|
|
}: DTHexagonProps) {
|
|
const animClass = animated
|
|
? animationType === 'rotate'
|
|
? 'dt-animate-spin'
|
|
: animationType === 'pulse'
|
|
? 'dt-animate-pulse'
|
|
: undefined
|
|
: undefined;
|
|
|
|
return (
|
|
<div
|
|
className={cx(getVariantClass(variant), animClass, className)}
|
|
style={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: size,
|
|
height: size,
|
|
position: 'relative',
|
|
opacity,
|
|
...(animated && animationDuration !== 2000
|
|
? { animationDuration: `${animationDuration}ms` }
|
|
: {}),
|
|
...style,
|
|
}}>
|
|
<svg
|
|
width={size}
|
|
height={size}
|
|
viewBox={`0 0 ${size} ${size}`}
|
|
style={{ position: 'absolute', top: 0, left: 0 }}>
|
|
<polygon
|
|
points={hexPoints(size - borderWidth)}
|
|
transform={`translate(${borderWidth / 2}, ${borderWidth / 2})`}
|
|
fill={filled ? (color || 'var(--dt-card-color, var(--color-primary))') : 'none'}
|
|
stroke={color || 'var(--dt-card-color, var(--color-primary))'}
|
|
strokeWidth={borderWidth}
|
|
/>
|
|
</svg>
|
|
{children && (
|
|
<div style={{ position: 'relative', zIndex: 1 }}>
|
|
{children}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|