Guide
Build a reading counter
Put Card and Button together on a working Astro page.
On this page
Before you start
- An Astro application with its dependencies installed.
- Bun and the pinned hulla CLI from the CLI quick start.
You’ll build a small page with Read a page and Reset buttons. The component files will live in your app, so you can read and edit the source as you go.
From your app’s root, initialize UI support if you haven’t already, then add the Astro components:
hulla ui init
hulla ui add --framework astro card buttonKeep the generated global stylesheet import in your shared layout. It supplies the colors, typography, focus rings, and other tokens used by the components. Check CLI configuration if your component paths differ from src/components.
Create src/pages/reading.astro. Import the installed parts and your app’s layout; adjust the layout import to match your project:
---
import Layout from '../layouts/Layout.astro'
import Button from '@/components/button/button.astro'
import Card from '@/components/card/card.astro'
import CardHeader from '@/components/card/card-header.astro'
import CardTitle from '@/components/card/card-title.astro'
import CardDescription from '@/components/card/card-description.astro'
import CardContent from '@/components/card/card-content.astro'
---Append the page markup below the closing --- in the same file:
<Layout>
<main class="mx-auto max-w-md p-6">
<h1 class="mb-6 text-2xl font-semibold">Reading counter</h1>
<Card>
<CardHeader>
<CardTitle aria-level={2}>One page at a time</CardTitle>
<CardDescription>Small progress still counts.</CardDescription>
</CardHeader>
<CardContent>
<p role="status" class="mb-4">
<span id="page-count">0</span> pages read
</p>
<div class="flex flex-wrap gap-3">
<Button id="read-page">Read a page</Button>
<Button id="reset-count" variant="outline">
Reset
</Button>
</div>
</CardContent>
</Card>
</main>
</Layout>The page chooses width, padding, and the gap between buttons. Card and Button choose their own appearance. The outline variant makes reset a quieter action without inventing a second button style.
Append this script after </Layout>. Astro bundles a normal <script>; a React or Solid island isn’t needed for these two listeners.
<script>
const count = document.querySelector<HTMLSpanElement>('#page-count')!
let pages = 0
const render = () => {
count.textContent = String(pages)
}
document.querySelector('#read-page')!.addEventListener('click', () => {
pages += 1
render()
})
document.querySelector('#reset-count')!.addEventListener('click', () => {
pages = 0
render()
})
</script>The status region announces changes to assistive technology. The native buttons already support keyboard activation and focus styling; your script only changes the count.
Start your app with its usual development command, then open /reading:
- Click Read a page twice. The count becomes
2. - Tab to Reset and press Enter. The count returns to
0. - Reload. It starts at
0because this demo keeps state in memory.
Continue with Composition and themes to change the page without overriding component states. Use the component guide to choose the next primitive.