Skip to content
Layout

Layout

DamaskTileBackdrop

Fixed ornamental tile lattice behind the page, plus a matching tiled glass pane.

npx bezel-add add damask-tile-backdrop

packages/ui/src/layout/DamaskTileBackdrop.tsx · 156 lines

"use client";

import type { CSSProperties, ReactNode } from "react";

export type DamaskTileBackdropProps = {
  /** Optional repeating tile image. If omitted, a CSS damask-like radial lattice is used. */
  tileSrc?: string;
  opacity?: number; // 0–1 pattern strength
  tileSize?: string; // CSS length, default 280px
  background?: string; // base wash color
  className?: string;
  style?: CSSProperties;
};

/** Fixed repeating ornamental tile under the whole viewport. */
export function DamaskTileBackdrop({
  tileSrc,
  opacity = 0.14,
  tileSize = "280px",
  background = "hsl(40 33% 97%)",
  className,
  style,
}: DamaskTileBackdropProps) {
  return (
    <>
      <style>{`
        .damask-tile-backdrop {
          pointer-events: none;
          position: fixed;
          inset: 0;
          z-index: 0;
          overflow: hidden;
          background-color: var(--dtb-bg);
        }
        .damask-tile-backdrop__pattern {
          position: absolute;
          inset: 0;
          opacity: var(--dtb-opacity);
          background-repeat: repeat;
          background-size: var(--dtb-size) var(--dtb-size);
          background-position: center;
        }
        .damask-tile-backdrop__pattern--css {
          background-image:
            radial-gradient(circle at 20% 20%, hsl(38 30% 55% / 0.22) 0 1px, transparent 1.5px),
            radial-gradient(circle at 80% 30%, hsl(5 35% 40% / 0.14) 0 1.2px, transparent 1.8px),
            radial-gradient(circle at 50% 70%, hsl(38 40% 50% / 0.18) 0 1.4px, transparent 2px),
            radial-gradient(hsl(38 20% 60% / 0.08) 1px, transparent 1px);
          background-size:
            calc(var(--dtb-size) * 0.5) calc(var(--dtb-size) * 0.5),
            calc(var(--dtb-size) * 0.66) calc(var(--dtb-size) * 0.66),
            var(--dtb-size) var(--dtb-size),
            calc(var(--dtb-size) * 0.22) calc(var(--dtb-size) * 0.22);
        }
        .damask-tile-backdrop__veil {
          position: absolute;
          inset: 0;
          background: color-mix(in srgb, var(--dtb-bg) 94%, transparent);
        }
      `}</style>
      <div
        className={["damask-tile-backdrop", className].filter(Boolean).join(" ")}
        style={
          {
            ...style,
            ["--dtb-bg" as string]: background,
            ["--dtb-opacity" as string]: String(opacity),
            ["--dtb-size" as string]: tileSize,
          } as CSSProperties
        }
        aria-hidden
      >
        <div
          className={
            tileSrc
              ? "damask-tile-backdrop__pattern"
              : "damask-tile-backdrop__pattern damask-tile-backdrop__pattern--css"
          }
          style={tileSrc ? { backgroundImage: `url(${tileSrc})` } : undefined}
        />
        <div className="damask-tile-backdrop__veil" />
      </div>
    </>
  );
}

export type TiledGlassSurfaceProps = {
  children: ReactNode;
  tileSrc?: string;
  opacity?: number;
  className?: string;
  style?: CSSProperties;
};

/** Local card/pane with the same tile + glass blur treatment. */
export function TiledGlassSurface({
  children,
  tileSrc,
  opacity = 0.12,
  className,
  style,
}: TiledGlassSurfaceProps) {
  return (
    <>
      <style>{`
        .tiled-glass-surface {
          position: relative;
          isolation: isolate;
          overflow: hidden;
          border-radius: 1rem;
          border: 1px solid hsl(40 12% 82% / 0.88);
          background: hsl(0 0% 100% / 0.55);
          backdrop-filter: blur(14px) saturate(1.05);
          -webkit-backdrop-filter: blur(14px) saturate(1.05);
          box-shadow:
            inset 0 1px 0 hsl(0 0% 100% / 0.42),
            0 10px 32px -14px hsl(60 4% 8% / 0.12);
        }
        .tiled-glass-surface__tile {
          position: absolute;
          inset: 0;
          z-index: 0;
          pointer-events: none;
          opacity: var(--tgs-opacity, 0.12);
          background-repeat: repeat;
          background-size: 180px 180px;
        }
        .tiled-glass-surface__tile--css {
          background-image:
            radial-gradient(circle at 30% 30%, hsl(38 30% 55% / 0.2) 0 1px, transparent 1.5px),
            radial-gradient(hsl(38 20% 60% / 0.07) 1px, transparent 1px);
          background-size: 90px 90px, 28px 28px;
        }
        .tiled-glass-surface__content {
          position: relative;
          z-index: 1;
        }
      `}</style>
      <div
        className={["tiled-glass-surface", className].filter(Boolean).join(" ")}
        style={{ ...style, ["--tgs-opacity" as string]: String(opacity) } as CSSProperties}
      >
        <div
          className={
            tileSrc
              ? "tiled-glass-surface__tile"
              : "tiled-glass-surface__tile tiled-glass-surface__tile--css"
          }
          style={tileSrc ? { backgroundImage: `url(${tileSrc})` } : undefined}
          aria-hidden
        />
        <div className="tiled-glass-surface__content">{children}</div>
      </div>
    </>
  );
}