/* 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 { --dt-card-progress: 0; --_card-pad: var(--space-6); display: flex; flex-direction: column; position: relative; isolation: isolate; background: var(--dt-card-color, var(--color-primary)); border: none; border-radius: 0; 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, [data-brand="dt"] .card::before { content: ''; position: absolute; inset: 0; background: var(--color-surface); z-index: -1; /* 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. Diagonal vertices use bevel-md + 3px (not bevel-md alone) so the perpendicular border width on the 45-degree diagonal visually matches the 3px straight edges despite sub-pixel antialiasing. */ clip-path: polygon(var(--bevel-sm) 3px, calc(100% - 3px) 3px, calc(100% - 3px) calc(100% - var(--bevel-md) - 3px), calc(100% - var(--bevel-md) - 3px) 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 top-to-bottom based on --dt-card-progress so the card visually "opens" as the bar descends. 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 bottom, 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-badge-bottom-right):not(.dt-card-badge), [data-brand="dt"] .card > *:not(.dt-badge-bottom-right):not(.dt-card-badge) { position: relative; z-index: 2; } /* Card Grow — stretch to fill available space in flex/grid containers. By default the body (content area) grows. Use .dt-card-grow-title to grow the header instead. */ .dt-card-grow { flex: 1 1 0%; height: 100%; } .dt-card-grow > .card-title { flex: 0 0 auto; } .dt-card-grow:not(.dt-card-grow-title) > :last-child { flex: 1 1 auto; } .dt-card-grow.dt-card-grow-title > .card-title { flex: 1 1 auto; } /* Card Header — storefront .card-header pattern 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 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(--dt-card-color, var(--color-primary)); color: var(--color-bg); font-weight: 900; 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>* { 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. Clip-path matches the card's inner surface bevel so the 3px structural border is visible at the diagonal corner. Badge overlays are excluded. */ [data-brand="dt"] .card-body-flush > *:not(.dt-badge-bottom-right):not(.dt-card-badge) { display: block; clip-path: polygon( 0% 0%, 100% 0%, 100% calc(100% - var(--bevel-md)), calc(100% - var(--bevel-md)) 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) 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: var(--color-surface-hover); } /* ============================================================================ 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)); } /* Colored badge variants — solid color fill with contrasting text. Dark mode: black text on bright neon fill. Light mode: white text on bright fill (via --color-bg which flips per theme). */ [data-brand="dt"] .badge-success { --badge-border-color: var(--color-success); --badge-fill: var(--color-success); color: var(--color-bg); } [data-brand="dt"] .badge-error { --badge-border-color: var(--color-error); --badge-fill: var(--color-error); color: var(--color-bg); } [data-brand="dt"] .badge-warning { --badge-border-color: var(--color-warning); --badge-fill: var(--color-warning); color: var(--color-bg); } [data-brand="dt"] .badge-info { --badge-border-color: var(--color-info); --badge-fill: var(--color-info); color: var(--color-bg); } /* Classic brand colored badges — same solid fill approach */ [data-brand="classic"] .badge-success { color: var(--color-bg); } [data-brand="classic"] .badge-error { color: var(--color-bg); } [data-brand="classic"] .badge-warning { color: var(--color-bg); } [data-brand="classic"] .badge-info { color: var(--color-bg); } /* Mode-variant badges — bridge .mode-* vars to badge color vars */ [data-brand="dt"] .badge.mode-normal { --badge-border-color: var(--mode-normal); --badge-fill: var(--mode-normal); color: var(--color-bg); } [data-brand="dt"] .badge.mode-emphasis { --badge-border-color: var(--mode-emphasis); --badge-fill: var(--mode-emphasis); color: var(--color-bg); } [data-brand="dt"] .badge.mode-warning { --badge-border-color: var(--mode-warning); --badge-fill: var(--mode-warning); color: var(--color-bg); } [data-brand="dt"] .badge.mode-success { --badge-border-color: var(--mode-success); --badge-fill: var(--mode-success); color: var(--color-bg); } [data-brand="dt"] .badge.mode-other { --badge-border-color: var(--mode-other); --badge-fill: var(--mode-other); color: var(--color-bg); } /* ============================================================================ 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 { 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( 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, .dt-bevel-media > model-viewer, .dt-bevel-media > iframe { 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)); } /* Video letterboxing — respect natural aspect ratio with black bars */ .dt-bevel-media > video { width: 100%; height: 100%; object-fit: contain; background: #000; } /* 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; 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; 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))); } /* ============================================================================ 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))); } /* Inner surface clip — inset from outer to create colored border frame */ .dt-bevel-drawer-right-inner { clip-path: polygon(3px calc(var(--bevel-lg) + 1px), calc(var(--bevel-lg) + 1px) 3px, 100% 3px, 100% calc(100% - 3px), calc(var(--bevel-lg) + 1px) calc(100% - 3px), 3px calc(100% - var(--bevel-lg) - 1px)); } .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%); } /* Inner surface clip — inset from outer to create colored border frame */ .dt-bevel-drawer-left-inner { clip-path: polygon(0% 3px, calc(100% - var(--bevel-lg) - 1px) 3px, calc(100% - 3px) calc(var(--bevel-lg) + 1px), calc(100% - 3px) calc(100% - var(--bevel-lg) - 1px), calc(100% - var(--bevel-lg) - 1px) calc(100% - 3px), 0% calc(100% - 3px)); } /* ============================================================================ 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); } /* ============================================================================ Card Color Modes — per-instance color overrides Sets --dt-card-color, --dt-card-color-selected, --dt-card-color-rgb, 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); --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); --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); --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); --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); --accent-mode: var(--mode-normal); } /* ============================================================================ Card Badge — bottom-right product type chip (LAB, BUNDLE, etc.) Source: dt-shopify-storefront CyberProductCard LabChip / BundleChip Positioned at card level so the card's clip-path bevels the badge corner. Consumer provides badge content/styling; card handles placement. ============================================================================ */ /* Legacy: standalone badge parent for non-card contexts */ .dt-badge-parent { position: relative; } /* Legacy: standalone badge positioning for non-card contexts */ .dt-badge-bottom-right { position: absolute; bottom: 8px; right: 8px; z-index: 4; padding: 2px calc(8px + 0.2rem) 2px 8px; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.1em; background-color: var(--dt-card-color, var(--color-primary)); color: var(--color-bg); } /* Card-level badge slot — positioned so the card's bevel clip-path clips the badge's bottom-right corner at 45 degrees. bottom/right: 3px aligns with the inner frame border edge. The badge extends into the bevel zone and gets clipped. */ .card > .dt-card-badge, .dt-bevel-card > .dt-card-badge { position: absolute; bottom: 3px; right: 3px; z-index: 4; } /* ============================================================================ 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; 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); }