Arizona Institute for Autism Astro Site
Static Astro + TypeScript migration of the public Arizona Institute for Autism website.
Quick Start
Run commands from this directory:
cd www
npm install
npm run dev
Astro prints the local development URL, normally:
http://localhost:4321
The development server watches source files and refreshes the browser after
edits. Stop it with Ctrl+C.
Environment Setup
Requirements:
- Node.js 20.19 or newer; the Nix shell currently supplies Node.js 22
- npm
This repository also has a Nix development shell at the repository root:
cd ..
nix --extra-experimental-features "nix-command flakes" develop
cd www
npm install
Copy the example environment file when testing the like/view counter or staging SEO behavior:
cp .env.example .env
The default values are:
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
PUBLIC_ALLOW_INDEXING=false
The site still renders when the AIA API is unavailable. Migration and staging
builds emit noindex,nofollow by default. PUBLIC_ALLOW_INDEXING=true enables
production publication policy, including per-page noindex exceptions. Exercise
that mode locally with PUBLIC_ALLOW_INDEXING=true npm run build; this does
not deploy anything or change environment files. Run npm run build afterward
to restore staging output. Only explicitly authorized production deployments
should publish indexing-enabled artifacts.
View And Build
Development Server
Use this while editing:
npm run dev
Useful development URLs include:
/
/aba-therapy
/autism-evaluations
/learner-social-club
/client-consultation
/library
/es
/es/library
/ar/library
Static Build
Generate the static site with staging indexing protection:
npm run build
The autonomous Codex sandbox denies /etc/hosts, so its Node runtime cannot
resolve localhost. Use this equivalent validation command only inside that
sandbox:
npm run build:sandbox
Build output is written to:
dist/
Preview Production Output
After a successful build:
npm run preview
Astro prints the preview URL, normally http://localhost:4321.
Validate Before Committing
npm run build
npm run audit:links
npm run audit:images
npm run audit:blog
The link audit reads dist/ after a successful build. If rendered output is
unavailable, it audits generated source routes, relative and root-relative
Markdown links, and public assets instead. It writes:
reports/broken-links.md
Editing Content
Normal content editing happens in Markdown:
src/content/pages/en
src/content/pages/es
src/content/pages/ar
src/content/blog/en
src/content/blog/es
src/content/blog/ar
src/content/authors/en
src/content/authors/es
src/content/authors/ar
Page and post frontmatter is validated by src/content.config.ts.
Example:
---
title: "Example Article"
description: "Short search and social description."
slug: "example-article"
canonical: "https://www.azinstitute4autism.com/library/example-article"
lang: "en"
translationKey: "example-article"
featuredImage: "/assets/images/example.webp"
alt: "Descriptive image alt text"
date: "2026-06-07"
author: "rula-diab"
category: "Library"
tags:
- ABA Therapy
draft: false
---
Article content goes here.
Set draft: true while preparing unpublished content.
Publication, Canonicals, and Sitemap
src/utils/publication-policy.ts owns route eligibility, normalized public
URLs, canonical validation, robots values, and sitemap eligibility.
src/utils/publication.ts loads the Astro collections into that manifest.
src/pages/[...slug].astro generates every content route from eligible entries,
including home and Library indexes; their existing presentation components are
under src/components/pages/. Shared SEO and the generated sitemap use that
same manifest. The route and source-link audits also reuse the policy.
draft: truemeans unpublished: no generated HTML route in either build mode. The Arabic homepage/arremains a draft; published Arabic Library routes remain available. Navigation does not link to the unpublished homepage.noindex: truekeeps a published route accessible, emitsnoindex,followin indexing-enabled builds, and excludes it from the sitemap. It defaults tofalse. Staging always overrides this withnoindex,nofollow.- An omitted
canonicalgets the normalized public URL fromsite.jsonand its local route. A supplied local canonical must match that route. Invalid canonicals, duplicate canonical targets, and route collisions fail validation. - Verified syndicated articles use their existing
canonicalfield for the external publisher URL. External-canonical routes are excluded from the sitemap even when they are not marked noindex. Do not use canonical values to create local routes or sitemap locations. src/pages/sitemap.xml.tsgeneratesdist/sitemap.xmlon every build. Staging emits an empty sitemap and does not advertise it inrobots.txt. Indexing-enabled builds include only eligible, indexable, self-canonical local routes. There is no hand-maintainedpublic/sitemap.xml.npm run generate:sitemapis a compatibility command that runs the Astro build; it no longer writes a separate public sitemap from frontmatter strings.
The verified noindex exceptions are /schedule-consultation, /employee-portal,
/library/community-highlight-meet-rula-diab, and
/library/new-aia-scottsdale-office. The last two also retain their verified
external canonicals. The dated evidence is in reports/migration-summary.md.
Run npm run test:publication for policy tests and isolated builds of the
actual application in staging and indexing-enabled modes. Fixtures test draft
content, draft home/Library indexes, missing and external canonicals, and
generated head/sitemap consistency without modifying the working site's output.
The policy is TypeScript; Node audit/test commands use --experimental-strip-types
for compatibility with the declared Node 22.12 minimum. Dependencies are unchanged.
After publication changes, build and run npm run audit:routes -- --offline
before npm run audit:routes -- --check. This creates a newly dated local
reconciliation using saved production observations, preserving earlier evidence.
Saved rendered metadata is specific to its build mode: after restoring the
normal staging build, reconcile that output again before checking it.
Links And Buttons
Use normal Markdown links for links within prose:
Read the [ABA therapy guide](/aba-therapy) for more information.
Prose links render as the live site's orange inline links. Do not place an ordinary Markdown link on its own line merely to make it look like a button.
Use .mdx and the Button component only for a source-verified call to action:
import Button from '../../../components/Button.astro';
<Button href="/client-consultation">Request an Appointment</Button>
<Button href="/services" variant="outlined">Explore Services</Button>
<Button href="https://example.com" target="_blank">External Action</Button>
Available Button variants are filled (default), outlined, and light.
Available sizes are default and small. A _blank target automatically adds
rel="noopener noreferrer".
Normal Markdown links remain preferred for ordinary page and article content.
Stable Heading Links
Astro automatically generates an ID for each Markdown heading. In .md files,
when a section needs a stable, editorially controlled fragment link, add an
explicit ID with Sätteri heading-attribute syntax:
## Accepted Insurance Carriers {#accepted-insurance-heading}
Link to it with the resulting fragment URL:
[View accepted carriers](/insurance#accepted-insurance-heading)
Keep explicit IDs unique within the page, lowercase, and hyphenated. Treat a
published ID as permanent so inbound links do not break when heading text is
edited. The installed MDX parser rejects this shorthand, independently of
directive parsing. In .mdx files, use an explicit HTML heading instead:
<h2 id="accepted-insurance-heading">Accepted Insurance Carriers</h2>
Markdown Directives
Sätteri directive parsing is disabled. The content corpus has no intentional
container (:::name), leaf (::name), or text (:name) directives, and no
directive transformation plugins are registered. Enabling parsing silently
removed literal text such as :1 in 1:1, :00 in times, and :Plan after
an adjacent colon. Heading attributes remain enabled.
Run npm run test:markdown after changing the Markdown/MDX configuration. It
builds temporary fixtures and the affected content through the actual Astro
configuration, checking ratios, punctuation, times, URLs, code, and explicit
heading IDs. It makes no production requests and leaves no fixture routes in
the site. Any future directive feature needs compatible rendering and must
preserve these literal-content checks.
Front Matter CMS
The project includes frontmatter.json for the Front Matter CMS VS Code
extension. Open the www folder as the editor workspace so its content folders
and media paths resolve correctly.
Edit The Homepage
The English and Spanish homepages are driven from the structured home:
frontmatter block in:
src/content/pages/en/index.md
src/content/pages/es/index.md
HomePage.astro renders those named sections in order. Use the section names
as the editing surface:
hero
servicesIntro
benefits
skills
insurance
esa
financialHelp
process
director
testimonials
Keep homepage copy in those named blocks so the page remains easy to scan in
Front Matter CMS and VS Code. src/content.config.ts validates the structure.
Images And Downloads
Store content-referenced assets in:
public/assets/images
public/assets/downloads
public/assets/media
Reference public assets from Markdown using root-relative paths:

Use src/assets/images for images imported directly by Astro components and
processed by Astro.
Components And Styling
Reusable page sections are in:
src/components
src/layouts
Global styles and design tokens are in:
src/styles/variables.css
src/styles/global.css
src/styles/rtl.css
Navigation, services, redirects, and site details are in:
src/data
Adding Content
Add An English Blog Post
- Create
src/content/blog/en/post-slug.md. - Add valid frontmatter and article Markdown.
- Place referenced images in
public/assets/images. - Run
npm run devand view/library/post-slug. - Run the production validation commands before committing.
Add A Translation
Use the same translationKey across translated entries:
src/content/blog/en/post-slug.md
src/content/blog/es/post-slug.md
src/content/blog/ar/post-slug.md
The URLs become:
/library/post-slug
/es/library/post-slug
/ar/library/post-slug
Arabic routes automatically render with lang="ar" and dir="rtl".
Do not publish unreviewed machine translations. Clearly mark placeholders.
Edit A Blog FAQ
Posts with live-style FAQ sections use .mdx and import:
import FAQAccordion from '../../../components/FAQAccordion.astro';
Edit the component's label, heading, and items data in the post. Keep
answer as plain text for JSON-LD and answerHtml as the matching visible
answer. Shared FAQ markup, styling, interaction, and schema generation live in
src/components/FAQAccordion.astro.
Reports And Utilities
Migration reports are stored in reports/.
Useful commands:
npm run crawl:live
npm run generate:sitemap
npm run generate:redirects
npm run audit:links
The live crawl is optional and may fail in restricted network environments.
Forms
Forms are intentionally static and do not submit anywhere. Before production:
- Select a form backend.
- Connect the forms.
- Add spam protection.
- Test validation, confirmation, and failure states.
Troubleshooting
astro: command not found
Dependencies are not installed:
npm install
npm TLS or certificate errors
Do not disable npm certificate verification. Run npm install from a normal
host terminal or trusted development environment with working CA certificates.
Link audit says build output is unavailable
Build first:
npm run build
npm run audit:links
npm run audit:images
npm run audit:blog
A content page is missing
Check:
- its Markdown file is in the correct language/content folder
draftisfalseslugis unique within that language and content type- frontmatter satisfies
src/content.config.ts