feat(about): Bento Grid layout + 7 content updates
Layout redesign:
- Add BentoGrid, BentoTile, DecoOrb components
- Add accent palette (purple/teal/mint/coral) to global tokens
- Rewrite about.astro using Bento Grid (12-col, surface variants)
- Decorative parallax blur orbs in 4 sections
- Value/process tiles use numerals 01-04 instead of emoji
Content updates per user feedback:
- Remove "Soloprenuer" emphasis
- Remove TOC ("เนื้อหาในหน้านี้") — not needed for short content
- Outsource + bot acknowledged as part of approach
- Replace AI stack with marketing intelligence tools
(ad spy, SEO gap, trend tracking)
- No fabricated stat numbers — placeholders show "—"
(only "5+ ปี" kept as verifiable, founded 2020)
- Post-delivery: small changes free, charge only for new features
- No Sprint methodology — flexible timeline, demo when ready
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
162
src/components/BentoTile.astro
Normal file
162
src/components/BentoTile.astro
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
.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>
|
||||
Reference in New Issue
Block a user