| name | templating-pages |
| description | Use when creating Astro layouts, page templates, or section components - enforces building-pages compliance and Astro conventions before code generation |
Templating Pages Skill
Use this skill when implementing page layouts, templates, or major UI sections for GeoVerity 2026.
Prerequisites
Before invoking this skill, you MUST have:
- ✅ Invoked
building-pagesskill - ✅ Completed compliance review template with verdict "PASS - Ready to ship"
- ✅ Documented the review in your implementation plan
If you have not completed the building-pages compliance review, STOP. Do that first.
Content Quality Gate (MANDATORY - v1.3.0)
Before finalizing ANY user-facing text (page titles, descriptions, body copy, CTAs, disclaimers), you MUST:
- ✅ Invoke
languagingskill (register compliance: B2-C1 for services, C1-C2 for Insights only) - ✅ Invoke
checking-crappy-writingv1.3.0 skill (AUTO-FIX violations) - ✅ Report auto-fixes to user in structured format
- ✅ User reviews and updates provenance to "human-edited"
- ✅ User sets _meta.contentStatus to "approved"
- ✅ Pass
npm run validate:governance
AUTO-FIX WORKFLOW (v1.3.0):
- Claude Code assistant automatically fixes AI artifacts (puffery, negative parallelism, emoji, meta-chatbot voice, awkward synonymy)
- Claude Code assistant reports changes to user (field-by-field)
- User reviews FIXES (not violations)
- User approves/rejects/edits
- Provenance tracking prevents AI text from reaching production
If provenance check FAILS, STOP. Content cannot be committed until human-reviewed.
See: .claude/skills/checking-crappy-writing/SKILL.md v1.3.0
Step 1: Mandatory Compliance Check
Verify you have:
- Building Pages compliance review completed (all PASS)
- Identified target locales (minimum: EN + ES)
- Confirmed mobile-first requirements (tap targets, gestures, performance)
- Design tokens or visual spec available
If any checkbox is unchecked, STOP and complete prerequisites.
Step 2: Choose the Correct Astro Pattern
GeoVerity uses specific Astro patterns. Select the appropriate one:
Pattern A: Full Page Layout
When: Creating a complete page shell (base HTML structure)
Guardrails:
- Must extend or live in
src/layouts/ - Must include
<!DOCTYPE html>,<html>,<head>,<body> - Must define
langanddirattributes from frontmatter - Must include skip link as first interactive element
- Must have
<slot />for page content
Bilingual Frontmatter (MANDATORY):
---
export interface Props {
lang: 'en' | 'es';
title: string;
description: string;
// ... other props
}
const { lang = 'en', title, description } = Astro.props;
const dir = lang === 'ar' ? 'rtl' : 'ltr'; // Future-proof for RTL
---
Example:
<!DOCTYPE html>
<html lang={lang} dir={dir}>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<meta name="description" content={description} />
<!-- JSON-LD structured data via generating-json-ld skill -->
</head>
<body>
<a href="#main" class="skip-link">Skip to main content</a>
<slot />
</body>
</html>
Pattern B: Section Component
When: Creating reusable sections (hero, features, CTA, etc.)
Guardrails:
- Must live in
src/components/sections/ - Must accept bilingual strings as props (no hardcoded English)
- Must use semantic HTML (
<section>,<article>,<nav>) - Must include
idoraria-labelfor landmark identification
Bilingual Props (MANDATORY):
---
export interface Props {
heading: string;
subheading?: string;
cta?: { text: string; href: string; };
lang?: 'en' | 'es';
}
const { heading, subheading, cta, lang = 'en' } = Astro.props;
---
Example:
<section aria-labelledby="hero-heading" class="hero">
<h1 id="hero-heading">{heading}</h1>
{subheading && <p class="subheading">{subheading}</p>}
{cta && (
<a href={cta.href} class="cta-button">
{cta.text}
</a>
)}
</section>
<style>
/* Mobile-first: base styles for smallest screens */
.hero {
padding: 1rem;
}
.cta-button {
/* Tap target: minimum 44×44px */
min-height: 44px;
padding: 12px 24px;
display: inline-block;
}
/* Progressive enhancement for larger screens */
@media (min-width: 768px) {
.hero {
padding: 2rem;
}
}
</style>
Pattern C: React Island (Interactive Component)
When: Adding client-side interactivity (forms, toggles, dynamic behavior)
Guardrails:
- Must live in
src/apps/(NOTsrc/components/) - Must export a manifest with bilingual strings
- Must render with
client:idleorclient:visible(NOTclient:load) - Must work with JavaScript disabled (progressive enhancement)
- Must follow React island compliance from Phase 4
Manifest (MANDATORY):
// src/apps/contact-form/manifest.ts
export const manifest = {
name: 'contact-form',
sensitivity: 'medium', // legal data collection
strings: {
en: {
nameLabel: 'Your Name',
emailLabel: 'Email Address',
submit: 'Send Message'
},
es: {
nameLabel: 'Su Nombre',
emailLabel: 'Correo Electrónico',
submit: 'Enviar Mensaje'
}
}
};
Hydration in Page:
---
import ContactForm from '@/apps/contact-form/ContactForm';
import { manifest } from '@/apps/contact-form/manifest';
const lang = Astro.currentLocale || 'en';
const strings = manifest.strings[lang];
---
<!-- Lazy hydration: only load when visible -->
<ContactForm client:visible strings={strings} />
<!-- No-JS fallback -->
<noscript>
<form action="/api/contact" method="POST">
<!-- Standard HTML form for accessibility -->
</form>
</noscript>
Step 3: Implement Mobile-First CSS
Rules:
- Base styles = mobile (320px-767px)
- Use
min-widthmedia queries for larger screens - Tap targets ≥ 44×44px (or 32×32px + 12px padding)
- Test gesture conflicts (swipe vs. scroll)
Example:
/* ❌ WRONG: Desktop-first */
.nav {
display: flex;
gap: 2rem;
}
@media (max-width: 768px) {
.nav {
flex-direction: column; /* Overriding desktop styles */
}
}
/* ✅ CORRECT: Mobile-first */
.nav {
display: flex;
flex-direction: column; /* Mobile default */
}
@media (min-width: 768px) {
.nav {
flex-direction: row;
gap: 2rem;
}
}
Tap Target Token:
:root {
--tap-target-min: 44px;
--tap-target-comfortable: 48px;
}
.button,
.nav-link,
.toggle {
min-height: var(--tap-target-min);
min-width: var(--tap-target-min);
}
Step 4: Bilingual Implementation Checklist
- All user-facing strings accept props (no hardcoded text)
-
langattribute propagates to all components -
dirattribute supports RTL (even if not used yet) - Date/number formatting uses
IntlAPIs - Image
alttext is localized - Form labels, placeholders, errors are localized
- ARIA labels and live regions are localized
Example i18n Pattern:
---
// Use centralized i18n utility (create if missing)
import { t } from '@/i18n/utils';
const { lang = 'en' } = Astro.props;
const strings = {
en: { title: 'Our Services', viewAll: 'View All Services' },
es: { title: 'Nuestros Servicios', viewAll: 'Ver Todos los Servicios' }
};
---
<section aria-labelledby="services-title">
<h2 id="services-title">{strings[lang].title}</h2>
<a href={`/${lang}/services`}>
{strings[lang].viewAll}
</a>
</section>
Step 5: Performance Optimization
Checklist:
- Images use
<picture>with WebP + fallback, or Astro's<Image> - Critical CSS inlined in
<head>(< 14KB) - Non-critical CSS loaded async
- Fonts preloaded with
font-display: swap - No render-blocking scripts in
<head>withoutdefer - React islands use
client:idleorclient:visible(NOTclient:load)
Astro Image Example:
---
import { Image } from 'astro:assets';
import heroImage from '@/assets/hero.jpg';
---
<Image
src={heroImage}
alt="GeoVerity team collaborating"
width={1200}
height={600}
loading="eager" /* Only for above-fold hero */
format="webp"
/>
Step 6: Accessibility Final Check
Before marking the template complete:
- Skip link visible on focus and functional
- Heading hierarchy is logical (
h1→h2→h3, no skips) - All interactive elements keyboard-accessible
- Focus rings visible (not
outline: nonewithout replacement) - Color contrast ≥ 4.5:1 for text, ≥ 3:1 for UI elements
- ARIA used only where semantic HTML insufficient
- Forms have
<label>for every<input> - Error messages associated with fields via
aria-describedby
Run local scan:
npm run build
node scripts/accessibility_scan.js
Step 7: Commit and Document
Git Commit Message Format:
feat(templates): add [component name] with bilingual support
- Implements [Pattern A/B/C] for [page/section]
- Passes building-pages compliance review (see PR description)
- Mobile-first CSS with tap targets ≥44px
- EN + ES strings via props
- LCP target ≤2.5s verified locally
Refs: geoverity-[bead-number]
PR Description Must Include:
- Building Pages Compliance Review (completed template from skill)
- Screenshots (mobile + desktop, EN + ES)
- Performance Metrics (Lighthouse scores or local measurements)
- Accessibility Notes (any deviations or special considerations)
Common Mistakes to Avoid
❌ Hardcoded English Strings
<h1>Welcome to GeoVerity</h1> <!-- Will fail localization validator -->
✅ Localized Props
<h1>{heading}</h1> <!-- Accepts EN or ES via props -->
❌ Desktop-First CSS
@media (max-width: 768px) {
/* Mobile overrides */
}
✅ Mobile-First CSS
/* Mobile base */
@media (min-width: 768px) {
/* Desktop enhancements */
}
❌ Eager Island Hydration
<ReactComponent client:load /> <!-- Blocks LCP -->
✅ Lazy Hydration
<ReactComponent client:visible /> <!-- Loads when scrolled into view -->
❌ Missing Tap Targets
.icon-button {
width: 24px;
height: 24px; /* Too small, fails mobile compliance */
}
✅ Proper Tap Targets
.icon-button {
width: 24px;
height: 24px;
padding: 12px; /* Effective hit area: 48×48px ✅ */
}
Integration with Other Skills
After completing this skill, you may need:
generating-json-ld- Add Schema.org structured data to all public pagesrequesting-code-review- Final review before PR submissionusing-astro- Framework-specific advanced patterns (collections, routing, SSR)
Enforcement Note
This skill works with the page-templating workflow. Skipping steps or bypassing conventions will:
- Fail CI -
validate_building_pages_review.jswill block merge - Fail localization validator - Hardcoded strings will be caught
- Fail accessibility scan - Missing
lang, poor contrast, etc. - Fail performance audit - LCP > 2.5s or CLS > 0.1
Every guardrail exists because someone tried to skip it and broke production.
Follow the checklist. Complete the compliance review. Ship with confidence.