Structure first, sentences next

In a new project, asking "what does the structure need to be?" before "what do I need to write?" is what makes content scalable, maintainable, and navigable.

When I built this portfolio, I made a deliberate decision to front-load structure before writing any substantive content. So I set up the IA, wired the sidebar, created placeholder pages, and got the CI/CD pipeline running in an intentional sequence.

Placeholder pages from day one

Even before I had any content, I created 32 placeholder pages, one for every planned page in the IA. The Docusaurus site builds cleanly from the first push because every page the sidebar references actually exists, even if it's just a heading and a one-line placeholder description.

This matters for a few reasons.

  1. The navigation structure is immediately visible and testable. If a section doesn't feel right at the structural level, I can catch that before I've invested writing time in it.
  2. The CI pipeline can run link checking without generating false failures from references to pages that don't exist yet.
  3. It signals to anyone who opens the repo that the project was planned, not improvised.

The placeholder approach also forced some useful thinking early. If I couldn't name a page and write a one-line description of what it would contain, that was a sign the page's purpose wasn't clear enough yet. Creating placeholders works as a lightweight content audit before the content exists.

This is what my first site scaffolding looked like:

tw-ia-content-system/
        ├── .github/
        │   └── workflows/
        │       ├── deploy.yml          ← lint + build + deploy pipeline
        │       ├── vale.yml            ← style check on push or PR
        │       ├── vale-pr.yml         ← Vale auto-fix and findings-report PR
        │       └── ai-draft.yml        ← AI draft generation workflow
        ├── docs/                       ← all portfolio content
        │   ├── index.md                ← home page
        │   ├── product-ideation/
        │   ├── information-architecture/
        │   ├── technical-documentation/
        │   │   ├── user-guide/
        │   │   ├── admin-guide/
        │   │   ├── knowledge-base/
        │   │   └── api-reference/
        │   ├── docs-as-code/
        │   ├── content-analytics/
        │   ├── ai-experiments/
        │   └── ux-writing/
        ├── src/
        │   ├── components/             ← custom React components (DemoEmbed, etc.)
        │   └── css/custom.css          ← theme overrides
        ├── static/
        │   ├── img/                    ← design artifacts and images
        │   └── dashboards/             ← standalone HTML dashboard files
        ├── styles/
        │   └── NimbusWiz/              ← custom Vale style rules
        ├── scripts/
        │   └── generate-draft.js       ← AI draft generation script
        ├── .vale.ini                   ← Vale configuration
        ├── docusaurus.config.js        ← site configuration
        └── sidebars.js                 ← navigation structure
        

Sidebar as an IA artifact

The sidebars file in a Docusaurus project is easy to treat as a configuration detail, the thing you only update when you add pages. I treated it as the primary IA artifact and a document of intent that reflects the full planned structure, with sections and all sub-pages at the correct nesting depth.

Any collaborator who opens the repo can see the entire information architecture without reading a single content page. The structure is legible before the content exists.

CI/CD live from the first commit

The CI/CD pipeline (Vale style linting, markdownlint, and link checking) runs on every push, configured before any substantial content existed.

There's a specific choice I made that I want to call out: continue-on-error: true on the quality checks, and if: false on the deploy job.

The continue-on-error setting means the pipeline reports failures without blocking the build. During active development, I want to know if something is failing, but I don't want a lint warning on a placeholder page to block me from pushing. The warning is information. It doesn't need to be a gate until the content is production-ready.

The if: false on the deploy job is the WIP flag. Lint runs on every push, so quality enforcement is active and the feedback loop is tight, but the deploy to production only happens when I remove that flag. It's a simple mechanism that makes the difference between "this is under construction" and "this is live" explicit in the configuration rather than in a README.

← Back to all posts