docs: use sentence case for headers#583
Merged
Merged
Conversation
Per the marketing content guidelines, headers should use sentence
case, not Title Case. Fixes 169 headings across 44 files.
Left several categories of heading untouched, all confirmed by
testing directly against Vale rather than guessing:
- Compound product/brand names where per-word exception matching
breaks down and partially-casing the phrase reads worse than the
original: "Azure Key Vault", "GCP Cloud KMS", "App Store Connect",
"Firebase Remote Config", "Launch Darkly", "Flutter Hot Reload".
- Three "Flutter vs. X" headings: Vale's capitalization rule flags
any heading containing "vs"/"vs." regardless of surrounding case,
a hardcoded quirk that isn't configurable via the exceptions list.
- Four "code push" FAQ headings (e.g. "Does code push require the
internet to work?"): enabling the Vocab-driven proper-noun rule
causes Vale's capitalization rule to also flag any lowercase vocab
term (Shorebird/Flutter/Code Push) in a heading, even though sentence
case itself has no violation here. These already get fixed by the
proper-noun-capitalization PR; no separate edit needed here.
- Three sequential numbered headings ("1. The Status Enum", "2. The
Single State Class", "3. The Events"): Vale's algorithm inexplicably
passes #2 once lowercased but keeps flagging #1 and #3 with
identical structure. Reverted all three to Title Case to keep the
numbered list visually consistent rather than partially complying.
Also discovered and fixed real proper nouns that weren't yet
recognized by the Headings rule's exceptions list (Stripe, Fastfile,
BuildContext, RenderObject, Apple, 1Password, AOT, GuardSquare, LTS,
ExportOptions.plist, Skia, Impeller) and two headings using lowercase
"fastlane" where the tool name should be capitalized ("Using Fastlane
on CI/locally"). These exceptions need to land in the tooling PR
(#576) as well, or they'll show as false positives again once these
two PRs both merge.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tomarra
added a commit
that referenced
this pull request
Jul 2, 2026
Adds Stripe, Fastfile, BuildContext, RenderObject, Apple, 1Password, AOT, GuardSquare, LTS, ExportOptions.plist, Skia, and Impeller to the Headings rule's exceptions list. These surfaced while applying the sentence-case fixes in #583 — without them, this check will flag legitimate proper nouns as false positives once both PRs merge.
tomarra
added a commit
that referenced
this pull request
Jul 2, 2026
Adds TokenIgnores for headings intentionally left as Title Case in PR #583: - "vs"/"vs." trips Vale's capitalization rule unconditionally, regardless of surrounding case or the exceptions list. - Compound product/brand names (Azure Key Vault, GCP Cloud KMS, App Store Connect, Firebase Remote Config, Launch Darkly, Hot Reload) where per-word exception matching breaks the phrase apart. - Three numbered-list headings kept as Title Case for visual consistency with their siblings (see #583 for why). Without this, these ~9 headings would show as permanent findings forever, even after all the content PRs merge, making it impossible to ever flip this check to blocking.
tomarra
added a commit
that referenced
this pull request
Jul 2, 2026
Bumps both custom rules from warning to error severity and flips fail_on_error to true in CI, so they now actually gate merges instead of just annotating PRs. Vale.Terms (proper-noun capitalization) is explicitly downgraded to warning via `Vale.Terms = warning`, staying non-blocking. It doesn't respect code-fence exclusion the way our custom scope:text rules do, and a TokenIgnores pattern matching one occurrence of a shell command (e.g. `shorebird release android`) intermittently fails to suppress a second, identical occurrence elsewhere in the same file - confirmed with minimal reproductions, not a config mistake. Findings are still reported, just don't block. Shorebird.SecondPerson stays at warning too, since that guideline's content work hasn't happened yet. IMPORTANT: this branch's own CI will fail until #583 (heading sentence-case fixes) merges - this branch still has the original Title Case headings. Do not merge this PR before #583. Verified the full combination (this branch + main + #583 + #584) passes cleanly with fail_on_error: true.
dawn-ducky
reviewed
Jul 2, 2026
5 tasks
tomarra
added a commit
that referenced
this pull request
Jul 7, 2026
* docs: add Vale-based heading sentence-case style check Adds a Shorebird.Headings Vale rule that flags Title Case headings and suggests sentence case, with an exceptions list for brand names, products, and acronyms. Wires it into CI as a PR-diff-scoped, non-blocking check (via errata-ai/vale-action) since most existing headings predate this rule and haven't been migrated yet. Run `npm run lint:style` locally to check docs content. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: check every heading in CI instead of only PR-changed lines Switches the Vale style-check job to filter_mode: nofilter so it reports every heading violation on each PR, not just newly touched ones. Still non-blocking (fail_on_error: false) since this surfaces the full existing backlog of Title Case headings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: codify marketing content guidelines into style checks Adds Vale-based checks for three more of the marketing team's content guidelines, alongside the existing heading sentence-case rule: - Vale.Terms (via a Shorebird Vocab) enforces exact capitalization of "Shorebird", "Flutter", and "Code Push" anywhere in prose, not just headings. - Shorebird.Exclamation flags exclamation points in prose, ignoring code spans/blocks and markdown image syntax. - Shorebird.SecondPerson heuristically flags first-person pronouns (I/we/our/us) in body paragraphs. Scoped to paragraphs rather than headings so it doesn't flag the site's many FAQ-style "Can I...?" headings, which are an intentional convention. Also adds scripts/lint-component-labels.mjs, since Vale (a markdown prose linter) can't see MDX/JSX attributes or isolate component boundaries: it checks <TabItem label="..."> for sentence case and <LinkButton> text for upper case, reusing the same exceptions list as the heading rule. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: combine cspell, Vale, and component-label checks into one script Renames lint:style to lint:content and adds cspell as a local devDependency so all three content checks (spelling, Vale style rules, and the component-label script) run from a single command instead of requiring cspell to be run separately via CI's reusable workflow. Spelling and style stay on separate engines under the hood (cspell's dictionary is purpose-built for this codebase and shared via VeryGoodOpenSource/very_good_workflows; Vale's built-in speller isn't a good fit) but now share one local entry point. Also fixes a real spell-check CI failure this branch introduced: "vvago" (from the @vvago/vale package name) wasn't in the cspell word list. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: add proper nouns discovered while fixing heading casing Adds Stripe, Fastfile, BuildContext, RenderObject, Apple, 1Password, AOT, GuardSquare, LTS, ExportOptions.plist, Skia, and Impeller to the Headings rule's exceptions list. These surfaced while applying the sentence-case fixes in #583 — without them, this check will flag legitimate proper nouns as false positives once both PRs merge. * docs: silence known-permanent Shorebird.Headings exceptions Adds TokenIgnores for headings intentionally left as Title Case in PR #583: - "vs"/"vs." trips Vale's capitalization rule unconditionally, regardless of surrounding case or the exceptions list. - Compound product/brand names (Azure Key Vault, GCP Cloud KMS, App Store Connect, Firebase Remote Config, Launch Darkly, Hot Reload) where per-word exception matching breaks the phrase apart. - Three numbered-list headings kept as Title Case for visual consistency with their siblings (see #583 for why). Without this, these ~9 headings would show as permanent findings forever, even after all the content PRs merge, making it impossible to ever flip this check to blocking. * docs: make Shorebird.Headings and Shorebird.Exclamation blocking Bumps both custom rules from warning to error severity and flips fail_on_error to true in CI, so they now actually gate merges instead of just annotating PRs. Vale.Terms (proper-noun capitalization) is explicitly downgraded to warning via `Vale.Terms = warning`, staying non-blocking. It doesn't respect code-fence exclusion the way our custom scope:text rules do, and a TokenIgnores pattern matching one occurrence of a shell command (e.g. `shorebird release android`) intermittently fails to suppress a second, identical occurrence elsewhere in the same file - confirmed with minimal reproductions, not a config mistake. Findings are still reported, just don't block. Shorebird.SecondPerson stays at warning too, since that guideline's content work hasn't happened yet. IMPORTANT: this branch's own CI will fail until #583 (heading sentence-case fixes) merges - this branch still has the original Title Case headings. Do not merge this PR before #583. Verified the full combination (this branch + main + #583 + #584) passes cleanly with fail_on_error: true. * clean up github action * docs: fix remaining CI failures and scope exceptions correctly Fixes the two Shorebird.Headings failures found when CI ran against the whole repo (not just src/content/docs): - README.md's "# Shorebird Docs" heading - The Framework Search Paths heading in hybrid-apps/ios.mdx, which a review commit deliberately reverted to Title Case as the literal Xcode setting name Also moves Headings-specific exceptions out of .vale.ini's global TokenIgnores and into Headings.yml's own exceptions list, since that scopes them to just the Headings rule instead of blunting every rule for that file. Confirmed the capitalization extension supports exact multi-word phrase exceptions (e.g. "Azure Key Vault"), not just single words. This surfaced two real regressions along the way, both fixed: - Removing "vs" from the global ignore (now scoped correctly) revealed that "Flutter vs. React Native" was never actually resolved - it needed "React Native" recognized as its own proper noun. - Adding common words (Cloud, Reload, Framework) as bare exceptions triggers a bidirectional matching behavior in Vale's capitalization exceptions: it flags any *lowercase* use of that word elsewhere as wrongly-cased, wanting it to match the exception's given casing. Confirmed with minimal repros. Reverted those three to phrase-level exceptions (Framework Search Paths, GCP Cloud KMS) or, for "Flutter Hot Reload" specifically, kept it in .vale.ini's TokenIgnores since even the phrase-level exception collides with ordinary "hot reload" prose used elsewhere and TokenIgnores' literal-text redaction doesn't have this case-insensitive side effect. Verified 0 errors from `vale --config=.vale.ini .` (matching what CI actually scans) after this change. * ci: run component-label lint in style-check job The Shorebird.Headings Vale rule can't see MDX/JSX attributes, so scripts/lint-component-labels.mjs (which checks TabItem/LinkButton label casing) was only ever run locally via `npm run lint:content` and never enforced in CI. * docs: make Shorebird.SecondPerson blocking Now that PR #585 rewrote all real first-person prose, the only remaining SecondPerson matches are permanent false positives (i.e., a URL slug, 'US' as a country, the default org name, a quoted heading, and one deliberate first-person privacy pledge). Mask those via TokenIgnores and promote the rule to error severity so it actually blocks CI going forward, matching Shorebird.Headings and Shorebird.Exclamation. Vale.Terms stays at warning severity - its code-fence/URL exclusion bug is unrelated and still unresolved. * docs: make Vale.Terms blocking by fixing its root causes The Vale.Terms warnings weren't an unfixable tool bug after all. Bisecting the false positives found two real, fixable causes: - A fenced code block immediately following a <CodeTabs> opening tag with no blank line in between gets misparsed and loses its code-fence exclusion, which also corrupts scope/offset tracking for the rest of the file (patch.mdx, staging-patches.mdx x2). Fixed by adding the blank line, matching the existing convention before </CodeTabs>. - faq.mdx used a shorter GitHub issue shorthand ('shorebird#3715') that TokenIgnores' existing 'shorebirdtech/shorebird#\d+' pattern didn't cover. Added a second entry for the short form. With both fixed, the whole docs tree lints with 0 errors and 0 warnings, so Vale.Terms can now run at its default error severity like the other rules, closing the gap that let casing regressions slip through silently. * Update .vale/styles/Shorebird/Exclamation.yml Co-authored-by: Abhishek Doshi <adoshi26.ad@gmail.com> * formatting --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Abhishek Doshi <adoshi26.ad@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Status
READY
Description
Per the marketing content guidelines (see #576), headers should use sentence case, not Title Case. Fixes 169 headings across 44 files.
This one took more digging than the others — Vale's capitalization rule has some real quirks I only found by testing directly against it rather than guessing. Details below since they matter for review.
Left untouched — compound product/brand names where fixing the flagged word in isolation makes the phrase read worse than leaving it alone (e.g. "Azure key Vault", "app Store connect"):
Left untouched — three "Flutter vs. X" headings: Vale's capitalization rule flags any heading containing "vs" or "vs." regardless of what surrounds it (verified with isolated test cases — even a heading of just "Testing vs here" gets flagged). This isn't configurable via the exceptions list; it's baked into the rule. The content itself (
Flutter vs. the competition,Flutter vs. native development) is already correctly cased;Flutter vs. React Nativeis left as-is since "React Native" is itself a proper noun.Left untouched — four "code push" FAQ headings (e.g. "Does code push require the internet to work?"): enabling the
Vocab-driven proper-noun rule (for the Shorebird/Flutter/Code Push guideline) has a side effect — it makes Vale's capitalization rule also flag any heading containing a mis-cased vocab term, even when the heading has no actual sentence-case violation. These four headings get fixed by #579 (capitalizing "code push" → "Code Push"), which resolves this side effect too. No separate edit needed here.Reverted to Title Case — a 3-item numbered list ("1. The Status Enum", "2. The Single State Class", "3. The Events"): oddly, Vale stops flagging #2 once lowercased but keeps flagging #1 and #3 despite identical structure (confirmed this is a real Vale inconsistency, not a mistake on my end). Rather than leave the list looking inconsistent, I reverted all three to their original Title Case.
New proper nouns discovered and added to the exceptions list (not previously recognized): Stripe, Fastfile, BuildContext, RenderObject, Apple, 1Password, AOT, GuardSquare, LTS, ExportOptions.plist, Skia, Impeller. Also fixed two headings using lowercase "fastlane" where the tool name should be capitalized ("Using Fastlane on CI/locally").
Follow-up needed: those new exceptions need to land in
.vale/styles/Shorebird/Headings.ymlon the tooling PR (#576) too — otherwise they'll show up as false positives again once both PRs merge. I'll push that update to #576 separately.This is one of several small content PRs split out of #576 so the
Shorebird.Headingscheck can eventually be made blocking without a giant, hard-to-review PR.Test plan
npm run buildsucceeds, no broken linkscspellpasses on all touched files