Skip to Content
Hashti Design System
Components

Tile

Flexible container primitives for interactive content blocks: standard, clickable, selectable, and expandable.

Tile

Tiles are versatile container building blocks designed to encapsulate modular units of content. Inspired by the ceramic tilework (kashi-kari) that creates geometric tapestries in Persian architectural facades, Hashti provides four specialized Tile variants: standard presentational Tile, navigational ClickableTile, form-integrated SelectableTile, and progressive-disclosure ExpandableTile.

TileCard-like structural surfaces with clickable states.
Static TileStructural Boundary Layer
Clickable TileNavigate to Portal Entrance →

Architectural Taxonomy

┌────────────────────────────────────────────────────────────────────────┐
│ Tile (Base container, semantic wrapper)                                │
├────────────────────────────────────────────────────────────────────────┤
│ ClickableTile (Hyperlink container with hover & focus ring)             │
├────────────────────────────────────────────────────────────────────────┤
│ SelectableTile (Multi-select card with integrated checkmark badge)     │
├────────────────────────────────────────────────────────────────────────┤
│ ExpandableTile (Above-the-fold summary with fold/unfold disclosure)    │
└────────────────────────────────────────────────────────────────────────┘
  • Modular Composability: Seamlessly hosts text, badges, icons, graphs, or interactive sub-elements.
  • Intrinsic Interactive Feedback: Clickable and Selectable tiles automatically apply high-contrast borders and active elevation shifts upon interaction.
  • Progressive Disclosure: ExpandableTile keeps dense dashboards clean by reserving complex diagnostics or raw payload traces behind a smooth disclosure button.

Import

import { Tile, ClickableTile, SelectableTile, ExpandableTile } from "@hashti/react";
import type {
  TileProps,
  ClickableTileProps,
  SelectableTileProps,
  ExpandableTileProps,
} from "@hashti/react";

Usage Examples

1. Standard Presentational Tile

The base Tile serves as a clean, rounded container surface. You can customize the rendered HTML element via the as prop (div, article, or section):

import { Tile } from "@hashti/react";

export function StandardTileDemo() {
  return (
    <Tile
      as="article"
      className="max-w-md p-5 bg-neutral-50 dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 rounded-lg"
    >
      <h3 className="font-medium text-base text-neutral-900 dark:text-neutral-100">
        System Overview
      </h3>
      <p className="mt-2 text-sm text-neutral-600 dark:text-neutral-400">
        All 14 regional nodes report healthy status with nominal thermal metrics.
      </p>
    </Tile>
  );
}

Use ClickableTile when an entire tile acts as a link to another page or external site:

import { ClickableTile, Icon } from "@hashti/react";

export function ClickableTileDemo() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-4 max-w-xl">
      <ClickableTile
        href="/docs/components/button"
        className="p-5 border border-neutral-200 dark:border-neutral-800 rounded-lg hover:border-blue-500 transition-colors"
      >
        <div className="flex justify-between items-start">
          <div>
            <h4 className="font-semibold text-neutral-900 dark:text-neutral-100">
              Button Primitives
            </h4>
            <p className="mt-1 text-xs text-neutral-500">
              Explore interactive button variants and sizes
            </p>
          </div>
          <Icon name="arrowRight" size={16} />
        </div>
      </ClickableTile>

      <ClickableTile
        href="/docs/foundations/theming"
        className="p-5 border border-neutral-200 dark:border-neutral-800 rounded-lg hover:border-blue-500 transition-colors"
      >
        <div className="flex justify-between items-start">
          <div>
            <h4 className="font-semibold text-neutral-900 dark:text-neutral-100">Theme System</h4>
            <p className="mt-1 text-xs text-neutral-500">
              Learn how Hashti switches between Frame and Core themes
            </p>
          </div>
          <Icon name="arrowRight" size={16} />
        </div>
      </ClickableTile>
    </div>
  );
}

3. SelectableTile (Multi-Selection Options)

SelectableTile renders a card with an integrated checkmark badge backed by a semantic HTML checkbox. When selected, the tile border and background update to reflect the selected state:

import { useState } from "react";
import { SelectableTile } from "@hashti/react";

export function SelectableTileDemo() {
  const [selectedAddons, setSelectedAddons] = useState<string[]>(["waf"]);

  const toggleAddon = (addon: string, isChecked: boolean) => {
    setSelectedAddons((prev) =>
      isChecked ? [...prev, addon] : prev.filter((item) => item !== addon),
    );
  };

  return (
    <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 max-w-xl">
      <SelectableTile
        id="tile-waf"
        name="addons"
        value="waf"
        selected={selectedAddons.includes("waf")}
        onChange={(checked) => toggleAddon("waf", checked)}
        className="p-4"
      >
        <div className="font-medium text-sm">Cloud WAF Protection</div>
        <div className="text-xs text-neutral-500 mt-1">
          Real-time Layer 7 DDoS mitigation and inspection
        </div>
      </SelectableTile>

      <SelectableTile
        id="tile-analytics"
        name="addons"
        value="analytics"
        selected={selectedAddons.includes("analytics")}
        onChange={(checked) => toggleAddon("analytics", checked)}
        className="p-4"
      >
        <div className="font-medium text-sm">Deep Telemetry</div>
        <div className="text-xs text-neutral-500 mt-1">
          Distributed tracing and continuous request logging
        </div>
      </SelectableTile>
    </div>
  );
}

4. ExpandableTile (Folding Content)

ExpandableTile presents summary content above the fold, accompanied by an accessible toggle button that smoothly unfolds detailed information below the fold:

import { ExpandableTile } from "@hashti/react";

export function ExpandableTileDemo() {
  return (
    <div className="max-w-lg">
      <ExpandableTile
        tileAboveTheFoldContent={
          <div className="p-4">
            <h4 className="font-semibold text-sm">Ingress Gateway Deployment</h4>
            <p className="text-xs text-neutral-500 mt-1">Status: Running · 10.0.12.44</p>
          </div>
        }
        tileBelowTheFoldContent={
          <div className="p-4 border-t border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900/50 text-xs font-mono">
            <div>Pod UID: 9a38f-be21-41c9</div>
            <div>CPU: 12% · Memory: 214 MB / 1024 MB</div>
            <div>TLS Cert Expiry: 82 days remaining</div>
          </div>
        }
      />
    </div>
  );
}

Props Reference

Standard Tile Props

PropTypeDefaultDescription
as'div' | 'article' | 'section''div'Rendered semantic HTML wrapper tag.
childrenReactNode—Content rendered within the tile.
classNamestring—Additional CSS class names.

ClickableTile Props

PropTypeDefaultDescription
hrefstring—Hyperlink destination URL.
targetstring—Browsing context target (e.g. '_blank').
relstring—Link relationship attribute (e.g. 'noopener noreferrer').
disabledbooleanfalseWhen true, disables the tile and sets aria-disabled="true".
childrenReactNode—Interactive card content.
classNamestring—Additional CSS class names.

SelectableTile Props

PropTypeDefaultDescription
selectedboolean—Controlled selection state.
defaultSelectedbooleanfalseInitial selection state for uncontrolled usage.
onChange(selected: boolean) => void—Callback invoked when selection changes.
disabledbooleanfalseDisables interaction on the selectable tile.
valuestring—Value submitted if placed in a native form.
namestring—Form field name for grouping.
idstring—Unique DOM identifier for the embedded checkbox input.
titlestring—Tooltip or title text for the input element.

ExpandableTile Props

PropTypeDefaultDescription
tileAboveTheFoldContentReactNode—Summary content permanently visible above the fold.
tileBelowTheFoldContentReactNode—Detailed content displayed when expanded.
expandedboolean—Controlled expansion state.
defaultExpandedbooleanfalseInitial expansion state for uncontrolled usage.
onToggle(expanded: boolean) => void—Callback triggered when the disclosure button is clicked.

Accessibility & Keyboard Interaction

  • ClickableTile:
    • Renders as an HTML <a> tag with focus ring styling.
    • Activated via keyboard Enter.
    • When disabled, applies aria-disabled="true" and tabIndex={-1}.
  • SelectableTile:
    • Encloses an accessible <input type="checkbox"> inside a <label> element.
    • Keyboard users can focus the input via Tab and toggle selection with Space.
    • Screen readers announce the label and checked state natively.
  • ExpandableTile:
    • Uses an explicit <button> control with aria-expanded={isExpanded}.
    • The button label alternates between “Show more” and “Show less”, ensuring clear affordance for screen readers.

Keyboard Interactions

KeyAction
Space / EnterSelects or expands tile when configured as selectable or expandable.
TabNavigates through interactive controls inside tile.

WAI-ARIA & Data Attributes

AttributeTargetDescription
[data-hovered]TilePresent on pointer hover.
[data-selected]TileActive when tile is selected.
[data-focused]TileActive on keyboard focus.