Files
moreminimore-astroreal/src/components/BentoTile.astro
Kunthawat Greethong b49931a87a fix(bento): force equal-width tiles via min-width: 0
Tiles in the same row had different widths because grid items
default to min-width: auto, which makes them grow to fit their
intrinsic content width instead of dividing the row equally.

Add min-width: 0, width: 100%, and box-sizing: border-box to
bento-tile so all tiles in a row are exactly equal width.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-09 14:19:18 +07:00

169 lines
5.6 KiB
Plaintext

---
/**
* BentoTile — a single bento grid cell.
*
* Props:
* span: 3 | 4 | 5 | 6 | 7 | 8 | 12 (default 6)
* rows: 1 | 2 | 3 (default 1)
* surface: 'white' | 'soft' | 'yellow' | 'purple' | 'purple-soft' | 'teal' | 'mint' | 'dark' | 'coral'
* minHeight: optional inline min-height CSS value
* eyebrow: optional small uppercase label above title
* title: optional H2 title
* reveal: boolean, animate on scroll into view (default true)
*
* Example:
* <BentoTile span={8} surface="yellow" eyebrow="วิธีทำงาน" title="ไม่ได้ทำงานแบบเดียวกับทุกที่">
* <p>...content...</p>
* </BentoTile>
*/
interface Props {
span?: 3 | 4 | 5 | 6 | 7 | 8 | 12;
rows?: 1 | 2 | 3;
surface?: 'white' | 'soft' | 'yellow' | 'purple' | 'purple-soft' | 'teal' | 'mint' | 'dark' | 'coral';
minHeight?: string;
eyebrow?: string;
title?: string;
reveal?: boolean;
class?: string;
}
const {
span = 6,
rows = 1,
surface = 'white',
minHeight,
eyebrow,
title,
reveal = true,
class: className = '',
} = Astro.props;
const spanClass = `span-${span}`;
const rowsClass = rows > 1 ? `rows-${rows}` : '';
const surfaceClass = `surface-${surface}`;
const revealClass = reveal ? 'reveal' : '';
---
<div
class:list={['bento-tile', spanClass, rowsClass, surfaceClass, revealClass, className]}
style={minHeight ? `min-height: ${minHeight};` : undefined}
>
{eyebrow && <div class:list={['tile-eyebrow', surface === 'dark' || surface === 'purple' || surface === 'teal' || surface === 'coral' ? 'inv' : '']}>{eyebrow}</div>}
{title && <h2 class:list={['tile-title', surface === 'dark' || surface === 'purple' || surface === 'teal' || surface === 'coral' ? 'light' : '']}>{title}</h2>}
<div class="tile-body">
<slot />
</div>
</div>
<style>
.bento-tile {
background: var(--color-white);
color: var(--color-black);
border: 1px solid var(--color-gray-200);
border-radius: var(--radius-xl);
padding: 32px;
position: relative;
overflow: hidden;
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease;
transform-style: preserve-3d;
min-height: 380px;
min-width: 0;
width: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.bento-tile:hover {
transform: translateY(-4px);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
}
/* Spans */
.span-3 { grid-column: span 3; }
.span-4 { grid-column: span 4; }
.span-5 { grid-column: span 5; }
.span-6 { grid-column: span 6; }
.span-7 { grid-column: span 7; }
.span-8 { grid-column: span 8; }
.span-12 { grid-column: span 12; }
.rows-2 { grid-row: span 2; }
.rows-3 { grid-row: span 3; }
@media (max-width: 1024px) {
.span-3, .span-4, .span-5 { grid-column: span 3; }
.span-6, .span-7, .span-8 { grid-column: span 6; }
.span-12 { grid-column: span 6; }
}
@media (max-width: 640px) {
[class*="span-"] { grid-column: span 1; }
.rows-2, .rows-3 { grid-row: span 1; }
}
/* Surface variants */
.surface-soft { background: var(--color-bg-soft); border-color: var(--color-gray-200); }
.surface-yellow { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-black); }
.surface-purple { background: var(--color-purple); border-color: var(--color-purple); color: var(--color-white); }
.surface-purple-soft { background: var(--color-purple-soft); border-color: var(--color-purple-soft); }
.surface-teal { background: var(--color-teal); border-color: var(--color-teal); color: var(--color-white); }
.surface-mint { background: var(--color-mint-soft); border-color: var(--color-mint-soft); }
.surface-coral { background: var(--color-coral); border-color: var(--color-coral); color: var(--color-white); }
.surface-dark { background: var(--color-black); border-color: var(--color-black); color: var(--color-white); }
/* Typography */
.tile-eyebrow {
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 2px;
opacity: 0.7;
margin-bottom: 12px;
}
.tile-eyebrow.inv {
opacity: 1;
color: var(--color-primary);
}
.tile-title {
font-family: var(--font-display);
font-size: clamp(22px, 3vw, 36px);
font-weight: 900;
line-height: 1.1;
margin-bottom: 16px;
}
.tile-title.light { color: var(--color-white); }
.tile-body { font-size: 16px; line-height: 1.7; }
.tile-body :global(p) { margin-bottom: 12px; }
.tile-body :global(p:last-child) { margin-bottom: 0; }
.tile-body :global(ul) { padding-left: 20px; margin-top: 12px; }
.tile-body :global(li) { margin-bottom: 8px; }
.tile-body :global(strong) { font-weight: 800; }
/* Light text on dark/colored surface */
.surface-dark .tile-body,
.surface-purple .tile-body,
.surface-teal .tile-body,
.surface-coral .tile-body {
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.surface-dark .tile-body :global(strong),
.surface-purple .tile-body :global(strong),
.surface-teal .tile-body :global(strong),
.surface-coral .tile-body :global(strong) { color: var(--color-primary); }
/* Yellow tile — always black text */
.surface-yellow .tile-title,
.surface-yellow .tile-body,
.surface-yellow .tile-body :global(strong) { color: var(--color-black); }
.surface-yellow .tile-eyebrow { color: var(--color-black); opacity: 0.7; }
/* Checkmark list variant (for .surface-yellow with .checklist) */
.tile-body :global(.checklist) {
list-style: none;
padding: 0;
}
.tile-body :global(.checklist li) {
padding-left: 0;
}
</style>