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.
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:
ExpandableTilekeeps 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>
);
}
2. ClickableTile (Navigational Hyperlink)
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
| Prop | Type | Default | Description |
|---|---|---|---|
as | 'div' | 'article' | 'section' | 'div' | Rendered semantic HTML wrapper tag. |
children | ReactNode | — | Content rendered within the tile. |
className | string | — | Additional CSS class names. |
ClickableTile Props
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | — | Hyperlink destination URL. |
target | string | — | Browsing context target (e.g. '_blank'). |
rel | string | — | Link relationship attribute (e.g. 'noopener noreferrer'). |
disabled | boolean | false | When true, disables the tile and sets aria-disabled="true". |
children | ReactNode | — | Interactive card content. |
className | string | — | Additional CSS class names. |
SelectableTile Props
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | — | Controlled selection state. |
defaultSelected | boolean | false | Initial selection state for uncontrolled usage. |
onChange | (selected: boolean) => void | — | Callback invoked when selection changes. |
disabled | boolean | false | Disables interaction on the selectable tile. |
value | string | — | Value submitted if placed in a native form. |
name | string | — | Form field name for grouping. |
id | string | — | Unique DOM identifier for the embedded checkbox input. |
title | string | — | Tooltip or title text for the input element. |
ExpandableTile Props
| Prop | Type | Default | Description |
|---|---|---|---|
tileAboveTheFoldContent | ReactNode | — | Summary content permanently visible above the fold. |
tileBelowTheFoldContent | ReactNode | — | Detailed content displayed when expanded. |
expanded | boolean | — | Controlled expansion state. |
defaultExpanded | boolean | false | Initial 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"andtabIndex={-1}.
- Renders as an HTML
- 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.
- Encloses an accessible
- ExpandableTile:
- Uses an explicit
<button>control witharia-expanded={isExpanded}. - The button label alternates between “Show more” and “Show less”, ensuring clear affordance for screen readers.
- Uses an explicit
Keyboard Interactions
| Key | Action |
|---|---|
| Space / Enter | Selects or expands tile when configured as selectable or expandable. |
| Tab | Navigates through interactive controls inside tile. |
WAI-ARIA & Data Attributes
| Attribute | Target | Description |
|---|---|---|
[data-hovered] | Tile | Present on pointer hover. |
[data-selected] | Tile | Active when tile is selected. |
[data-focused] | Tile | Active on keyboard focus. |