Skip to content

feat(website): SEO, GEO, and accessibility improvements#294

Merged
rafaeltonholo merged 28 commits intomainfrom
chore/seo-geo-improvements
Apr 3, 2026
Merged

feat(website): SEO, GEO, and accessibility improvements#294
rafaeltonholo merged 28 commits intomainfrom
chore/seo-geo-improvements

Conversation

@rafaeltonholo
Copy link
Copy Markdown
Owner

@rafaeltonholo rafaeltonholo commented Apr 2, 2026

Summary

  • Add SEO infrastructure: structured data (JSON-LD), Open Graph/Twitter meta tags, canonical URLs, breadcrumbs, and sitemap generation
  • Add GEO/AI visibility: llms.txt and llms-full.txt for AI crawler discovery, AI-friendly robots.txt directives
  • Add FAQ and Alternatives documentation pages with FAQPage structured data
  • Improve accessibility: ARIA keyboard navigation on dropdown menu, semantic H1/H2 restructure, mobile ToC layout fix, nav landmarks on footer links
  • Add configure-bot GitHub App action and post-deploy LAST_MODIFIED commit workflow

Changes

SEO:

  • Shorten page titles to fit within 60 chars; trim meta descriptions under 160 chars
  • Restructure hero: H1 for brand, H2 for keyword-rich subheadline
  • Add sameAs GitHub link to WebSite schema; harden JSON-LD control char escaping
  • Align FAQ section headings with structured data question text
  • Auto-generate sitemap.xml from @Page files with per-page priority/changefreq
  • Read sitemap lastmod from app.properties (configuration cache compatible)

GEO:

  • Add llms.txt and llms-full.txt templates with build-time version substitution
  • Add rel="llms" discovery link tags to HTML head

Accessibility:

  • Add ArrowUp/Down/Home/End keyboard navigation to docs dropdown menu
  • Add aria-label to dropdown menu panel
  • Use Modifier.role() instead of attr("role", ...) per project convention
  • Wrap mobile ToC links in Column for proper vertical layout
  • Add <nav> landmark with aria-label on footer docs links

Footer:

  • Add internal docs links (shared docLinkEntries used by both nav dropdown and footer)
  • Add CSS ::before middle-dot separators between doc links
  • Fix attribution text wrapping by switching from flex Row to inline Span
  • Reduce footer padding on mobile for better proportions

CI:

  • Add configure-bot composite action for svg-to-compose-bot[bot] Git identity
  • Add update-last-modified job that commits LAST_MODIFIED after successful deploy

Summary by CodeRabbit

  • New Features

    • Added FAQ and Alternatives docs pages, How‑To structured data, and sitemap generation exposing last-modified dates.
  • Improvements

    • Enhanced SEO/meta tags (Open Graph, Twitter, locale), breadcrumbs and JSON‑LD; typography and layout tweaks.
    • Accessibility: image alt text and keyboard navigation for menus.
    • Docs UI: new table styles, TOC/layout refinements, version badge, footer and nav updates.
  • Documentation

    • Expanded CLI/Gradle/plugin reference and added a full end‑user documentation template.
  • Chores

    • Automated last-modified updates and configured bot identity for site commits.

- Creates /docs/alternatives with sections covering manual conversion, Android Studio import, community tools, and a feature comparison table.
- Adds the Alternatives card to the docs index.
- Add missing `import java.time.LocalDate` to build.gradle.kts to fix script compilation error (Unresolved reference 'time')
- Fix PascalCase-to-kebab-case conversion in sitemap generator; premature `.lowercase()` was applied before the regex ran, so GradlePlugin.kt was emitted as /docs/gradleplugin instead of /docs/gradle-plugin
- Use InlineCode for class names, CLI flags, file extensions, attributes
- Use CodeBlock for command examples in FAQ
- Use SiteLinkStyleVariant for links
- Fix Android Studio Import: mention CMP workaround via composeResources
- Add custom parser vs Android Studio algorithm context
- Fix comparison table: SVG subset (not AVG), add SVG feature coverage row
- Add SVGO/Avocado install instructions to optimization FAQ
- Add FAQ and Alternatives to docs nav dropdown
Escape all JSON spec control characters (U+0000-U+001F) in escapeJsonString(), not just the common five. Add sameAs GitHub link to WebSiteStructuredData for stronger entity recognition.
- Trim title tags to fit within 60 characters (with suffix).
- Trim meta descriptions for Alternatives and Docs Index to stay under 160 chars.
- Enrich Docs Index title with keyword context.
Replace attr("role", "table") with Modifier.role("table") per project
convention. Remove unused PlatformTable* aliases that just re-assigned
DocsStyles values.
Match visible H2 headings to the question wording used in the FAQPage
JSON-LD schema so screen readers and search engines see consistent
content.
Add ArrowUp/Down/Home/End keyboard navigation for ARIA menu compliance.
Add aria-label to menu panel. Extract doc link entries into a shared
list to prevent drift with footer DocsLinks.
Move version badge outside H1 to keep heading keyword-focused. Change
subheadline from div to H2 with fontWeight(Normal) so search engines
give heading-level weight to the conversion keywords.
Add DocsLinks component using shared docLinkEntries in a nav landmark.
Use CSS ::before middle-dot separators between links. Change
Attribution from flex Row to inline Span for natural word wrapping.
Reduce footer padding on mobile for better proportions.
ToC links inside CollapsibleSection rendered inline without a flex
container, causing them to run together. Wrap in Column for proper
vertical stacking.
Reusable action that configures Git user.name and user.email with the
svg-to-compose-bot[bot] identity using the GitHub App ID.
Replace providers.exec git call with LAST_MODIFIED from app.properties.
The deploy workflow updates the value before build, then commits it
via svg-to-compose-bot after successful deployment. Uses GITHUB_TOKEN
for the push to avoid triggering other workflows.
@rafaeltonholo rafaeltonholo added this to the 2.2.0 milestone Apr 2, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

Walkthrough

Adds site-generation tasks (sitemap, llms-full), new docs pages and structured-data types, UI/accessibility refinements, a GitHub composite action to configure a bot identity, and a workflow job that updates app.properties LAST_MODIFIED and conditionally commits it. (49 words)

Changes

Cohort / File(s) Summary
GitHub actions & workflows
\.github/actions/configure-bot/action.yml, \.github/workflows/deploy-website.yml
New composite action to set git bot identity (app-id); deploy-website.yml now computes UTC date in export, regenerates sitemap, exposes last-modified, and adds update-last-modified job that updates app.properties and conditionally commits using the bot.
Site properties & generated assets
app.properties, website/site/.gitignore, website/site/src/jsMain/resources/public/sitemap.xml
Added LAST_MODIFIED property; updated .gitignore to ignore generated llms* and sitemap.xml; removed static sitemap.xml.
Build script & generation tasks
website/site/build.gradle.kts
Reworked tasks: declared outputs and wrote dual-location llms.txt/.well-known, added generateLlmsFullTxt, added generateSitemap (scans @Page files and requires LAST_MODIFIED), tightened a task type, and wired tasks into jsProcessResources.
LLMs templates & resources
website/site/src/jsMain/resources/llms-full.txt.template, .../llms.txt.template
Added comprehensive llms-full template; minor wording updates to existing llms.txt.template.
SEO, meta & structured data
website/site/src/jsMain/kotlin/.../components/atoms/SeoHead.kt, .../StructuredData.kt
Changed title format to `title
Docs pages & new content
website/site/src/jsMain/kotlin/.../pages/docs/*.kt, website/site/src/jsMain/kotlin/.../components/organisms/docs/*
Added FAQ and Alternatives pages/composables, HowTo structured data on multiple docs pages, comparison table and FAQ content components, CLI/Gradle docs metadata updates.
Docs styling & tables
website/site/src/jsMain/kotlin/.../components/organisms/docs/DocsStyles.kt, .../CliDocsContent.kt
Added shared table styles (DocsTableStyle, DocsTableHeaderStyle, DocsTableCellStyle) and migrated page tables to use them.
Layout, accessibility & UI tweaks
website/site/src/jsMain/kotlin/.../AppEntry.kt, .../layouts/DocsLayout.kt, .../HeroSection.kt, .../NavHeader.kt
Renamed init to initSilk, set document.documentElement.lang, switched DocsLayout content to Column spacing, added version badge, adjusted headings, added image alt text and new nav links (FAQ, Alternatives).
Footer & navigation improvements
website/site/src/jsMain/kotlin/.../components/molecules/DocNavDropdown.kt, .../footer/Links.kt, .../footer/Attribution.kt, .../footer/Footer.kt, .../footer/LinksAndAttributionStyle.kt, .../footer/LogoAndDescription.kt
Added keyboard nav and aria labels to docs dropdown; exposed docLinkEntries; added DocsLinks/DocsLinkVariant to footer; refactored attribution and footer layout; set logo image alt to empty string.
Various UI/content adjustments
website/site/src/jsMain/kotlin/.../components/molecules/*, .../theme/AppStyles.kt, .../pages/Index.kt
Small UI/content edits: display font-size clamp tweak, homepage/docs title/description updates, structured-data additions, and assorted component refinements.

Sequence Diagram

sequenceDiagram
    participant GH as "GitHub Actions"
    participant Export as "export job"
    participant Gradle as "Gradle site tasks"
    participant FS as "File System / Repo"
    participant Deploy as "deploy job"
    participant Update as "update-last-modified job"
    participant Bot as "Configure-bot action"

    GH->>Export: workflow starts
    Export->>Gradle: run site generation tasks (generateLlmsTxt, generateLlmsFullTxt, generateSitemap)
    Gradle->>FS: read `app.properties` (LAST_MODIFIED)
    Gradle->>FS: write generated files (public/*.txt, .well-known/*, sitemap.xml)
    Export->>Export: compute UTC TODAY, set job output `last-modified`
    Export->>Deploy: continue deploy
    GH->>Update: trigger update-last-modified (needs: deploy, export)
    Update->>FS: checkout main
    Update->>Bot: run configure-bot (`app-id`)
    Update->>FS: replace LAST_MODIFIED in `app.properties`
    Update->>FS: run `git diff`
    alt changes detected
        Update->>FS: git commit & push (using bot identity)
    else no changes
        Update-->>Update: skip commit/push
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes in the PR: SEO improvements (structured data, sitemap, meta tags), GEO/AI visibility enhancements (llms.txt files), and accessibility improvements (keyboard navigation, ARIA labels). It accurately represents the primary objectives without being misleading or overly vague.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/seo-geo-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (4)
website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt (1)

236-290: Consider extracting the repetitive Td styling into a helper function.

Each Td cell applies the same modifier chain with only textAlign varying. This could be simplified with a helper function to reduce duplication.

♻️ Optional: Extract common cell styling
`@Composable`
private fun DataCell(
    text: String,
    palette: SitePalette,
    textAlign: TextAlign = TextAlign.Center,
) {
    Td(
        attrs = DocsTableCellStyle
            .toModifier()
            .textAlign(textAlign)
            .color(palette.onSurface)
            .toAttrs(),
    ) {
        Text(text)
    }
}

Then usage becomes:

Tr(attrs = backgroundModifier.toAttrs()) {
    DataCell(row.feature, palette, TextAlign.Start)
    DataCell(row.svgToCompose, palette)
    DataCell(row.manualCoding, palette)
    DataCell(row.androidStudio, palette)
    DataCell(row.otherTools, palette)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt`
around lines 236 - 290, The ComparisonTableRow contains repeated Td styling;
extract that common modifier chain into a helper composable (e.g., DataCell)
that accepts the cell text (String), palette (SitePalette), and an optional
textAlign: TextAlign = TextAlign.Center, build the Td with
DocsTableCellStyle.toModifier().textAlign(textAlign).color(palette.onSurface).toAttrs()
and emit Text(text) inside; then replace the five inline Td blocks in
ComparisonTableRow with DataCell(row.feature, palette, TextAlign.Start) and
DataCell(row.svgToCompose, palette), DataCell(row.manualCoding, palette),
DataCell(row.androidStudio, palette), DataCell(row.otherTools, palette).
website/site/src/jsMain/resources/llms-full.txt.template (1)

302-319: Consider adding an "AI assistance" section with common prompt patterns.

Since this file targets LLMs, including examples of how to effectively prompt for SVG-to-Compose help could enhance AI assistants' effectiveness when users ask questions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/site/src/jsMain/resources/llms-full.txt.template` around lines 302 -
319, Add a new "AI assistance" subsection to the LLM-targeted template that
provides a few concise prompt patterns and example prompts for common tasks
(e.g., "convert this SVG to Compose with optimized paths", "generate a
multi-layer icon with tintable colors", "reduce path commands while preserving
shape") so AI agents can produce consistent, high-quality outputs; include
guidance on expected inputs (SVG, desired target platform/size, color/tint
rules) and example completions to illustrate desired responses — update the
llms-full.txt.template content near the "Links" section by inserting this new
subsection headed "AI assistance" with 3–5 prompt patterns and 2 short example
prompts + expected outputs.
website/site/build.gradle.kts (1)

174-177: PascalCase to kebab-case conversion handles only single transitions.

The regex ([a-z])([A-Z]) handles GradlePlugingradle-plugin but won't correctly handle consecutive uppercase like CLITool (would become c-l-i-tool instead of cli-tool).

Given the current page naming conventions in the codebase (Faq, Alternatives, Cli, GradlePlugin), this should work correctly. Just noting for awareness if new pages with consecutive uppercase are added.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/site/build.gradle.kts` around lines 174 - 177, The current
segment.replace using Regex("([a-z])([A-Z])") only handles single lower→upper
transitions and splits acronyms incorrectly; update the transformation to first
insert a hyphen between an uppercase-acronym and a following CapitalizedWord
(e.g. use Regex("([A-Z]+)([A-Z][a-z])") -> "$1-$2"), then run the existing
lower→upper replacement (e.g. Regex("([a-z0-9])([A-Z])") -> "$1-$2"), and
finally .lowercase() so names like "CLITool" become "cli-tool" while preserving
existing conversions (modify the segment.replace pipeline accordingly).
website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt (1)

69-75: Include the docs landing page in the shared entries.

DocsLinks() in website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt already has to special-case /docs, while this dropdown renders only docLinkEntries. Moving the overview page into the shared list would keep both navs aligned and make the docs index reachable from the desktop Docs menu too.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`
around lines 69 - 75, Add the docs landing page (path "/docs", label "Overview"
or "Docs") to the shared docLinkEntries list so the desktop Docs dropdown and
the footer's DocsLinks() stay in sync; update the list defined as docLinkEntries
(which contains DocLinkEntry instances) to include a DocLinkEntry for "/docs" so
the docs index becomes reachable from the desktop Docs menu as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/actions/configure-bot/action.yml:
- Around line 2-4: The action description says "Generates a GitHub App token"
but the implementation only configures Git identity; either update the
description string under the description key to reflect that it only configures
Git with the bot identity, or implement actual token creation (e.g., add a step
using tibdex/github-app-token or actions/create-github-app-token and expose the
token to git) so the behavior matches the description; check references to
GITHUB_TOKEN and the update-last-modified job to ensure they use the correct
token (GITHUB_TOKEN if relying on default checkout vs the newly created app
token) and adjust steps accordingly.

In @.github/workflows/deploy-website.yml:
- Around line 101-117: The job update-last-modified references
needs.export.outputs.last-modified but only lists needs: deploy; update the
needs array for the update-last-modified job to include the export job (e.g.
needs: [deploy, export]) so that the output needs.export.outputs.last-modified
is defined and available to the job.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`:
- Around line 211-255: Add an "Escape" branch to the existing onKeyDown handler
(the attrsModifier onKeyDown in DocNavDropdown.kt) that calls
event.preventDefault() and event.stopPropagation(), closes the menu (invoke the
component's close routine such as closeDropdown()/onClose()/setOpen(false) —
whichever is used in this component) and returns focus to the dropdown trigger
(use the existing trigger ref if present, or add/store a triggerRef and call
triggerRef?.focus() or querySelector the trigger element with its ARIA
selector). Ensure this new case is placed alongside the
"ArrowDown"/"ArrowUp"/"Home"/"End" branches and targets menu items with
role='menuitem'.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt`:
- Around line 103-106: The separator dot (::before) on each DocsLinks item shows
up on wrapped lines; update Links.kt so the Row that uses
.flexWrap(FlexWrap.Wrap) also applies a distinguishing CSS class/modifier (e.g.,
"wrap" or "no-separators") and then change the DocsLinks item styling so the
::before pseudo-element is suppressed when its parent has that class (i.e., hide
::before under the wrapping container), relying on the existing .gap for spacing
instead; locate the Row usage and the DocsLinks component to add the container
class and adjust the selector that generates the separator.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/LogoAndDescription.kt`:
- Around line 33-36: The Image call in the LogoAndDescription composable uses a
redundant alt text; make the logo decorative by setting the alt to an empty
string (alt = "") in the Image(...) invocation (the same call that currently
sets src = "/images/s2c-icon.svg" and modifier = Modifier.size(1.25.cssRem)) so
the adjacent visible product label remains the accessible name.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt`:
- Around line 83-98: The section ID in WhatIsS2cSection should match the FAQ
question slug: change DocSection(id = "what-is-s2c", ...) to id =
"what-is-svg-to-compose" inside the WhatIsS2cSection composable, and then update
the corresponding TocEntry/Toc list in Faq.kt (the entry associated with the
"What is SVG to Compose?" question in faqQuestions/TocEntry) to use the same
"what-is-svg-to-compose" fragment so the TOC and section IDs remain consistent
for SEO and URL fragments.

---

Nitpick comments:
In `@website/site/build.gradle.kts`:
- Around line 174-177: The current segment.replace using Regex("([a-z])([A-Z])")
only handles single lower→upper transitions and splits acronyms incorrectly;
update the transformation to first insert a hyphen between an uppercase-acronym
and a following CapitalizedWord (e.g. use Regex("([A-Z]+)([A-Z][a-z])") ->
"$1-$2"), then run the existing lower→upper replacement (e.g.
Regex("([a-z0-9])([A-Z])") -> "$1-$2"), and finally .lowercase() so names like
"CLITool" become "cli-tool" while preserving existing conversions (modify the
segment.replace pipeline accordingly).

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`:
- Around line 69-75: Add the docs landing page (path "/docs", label "Overview"
or "Docs") to the shared docLinkEntries list so the desktop Docs dropdown and
the footer's DocsLinks() stay in sync; update the list defined as docLinkEntries
(which contains DocLinkEntry instances) to include a DocLinkEntry for "/docs" so
the docs index becomes reachable from the desktop Docs menu as well.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt`:
- Around line 236-290: The ComparisonTableRow contains repeated Td styling;
extract that common modifier chain into a helper composable (e.g., DataCell)
that accepts the cell text (String), palette (SitePalette), and an optional
textAlign: TextAlign = TextAlign.Center, build the Td with
DocsTableCellStyle.toModifier().textAlign(textAlign).color(palette.onSurface).toAttrs()
and emit Text(text) inside; then replace the five inline Td blocks in
ComparisonTableRow with DataCell(row.feature, palette, TextAlign.Start) and
DataCell(row.svgToCompose, palette), DataCell(row.manualCoding, palette),
DataCell(row.androidStudio, palette), DataCell(row.otherTools, palette).

In `@website/site/src/jsMain/resources/llms-full.txt.template`:
- Around line 302-319: Add a new "AI assistance" subsection to the LLM-targeted
template that provides a few concise prompt patterns and example prompts for
common tasks (e.g., "convert this SVG to Compose with optimized paths",
"generate a multi-layer icon with tintable colors", "reduce path commands while
preserving shape") so AI agents can produce consistent, high-quality outputs;
include guidance on expected inputs (SVG, desired target platform/size,
color/tint rules) and example completions to illustrate desired responses —
update the llms-full.txt.template content near the "Links" section by inserting
this new subsection headed "AI assistance" with 3–5 prompt patterns and 2 short
example prompts + expected outputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1cfb096c-0288-4a7f-b40f-6fc8ef3937c3

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4a11f and a49ab7b.

📒 Files selected for processing (31)
  • .github/actions/configure-bot/action.yml
  • .github/workflows/deploy-website.yml
  • app.properties
  • website/site/.gitignore
  • website/site/build.gradle.kts
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/AppEntry.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/SeoHead.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/layouts/DocsLayout.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Attribution.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/LogoAndDescription.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/HeroSection.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/NavHeader.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/CliDocsContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/DocsStyles.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/footer/Footer.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/footer/LinksAndAttributionStyle.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/Index.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Alternatives.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Cli.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Faq.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/GradlePlugin.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Index.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/theme/AppStyles.kt
  • website/site/src/jsMain/resources/llms-full.txt.template
  • website/site/src/jsMain/resources/llms.txt.template
  • website/site/src/jsMain/resources/public/sitemap.xml
💤 Files with no reviewable changes (1)
  • website/site/src/jsMain/resources/public/sitemap.xml

Comment on lines +83 to +98
@Composable
private fun WhatIsS2cSection() {
DocSection(id = "what-is-s2c", title = "What is SVG to Compose?") {
Span(attrs = DocsBodyTextStyle.toAttrs()) {
Text(
"SVG to Compose is a Kotlin Multiplatform tool that converts SVG and Android XML " +
"Drawable files into Jetpack Compose ",
)
InlineCode("ImageVector")
Text(
" code. It eliminates the manual, error-prone process of hand-writing " +
"ImageVector builder code. Available as a CLI tool, a Gradle plugin, and a library.",
)
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Section ID mismatch with FAQ question title.

The section uses id = "what-is-s2c" but the corresponding question in faqQuestions is "What is SVG to Compose?". For better SEO consistency and URL fragment readability, consider using id = "what-is-svg-to-compose".

🔧 Suggested fix
 `@Composable`
 private fun WhatIsS2cSection() {
-    DocSection(id = "what-is-s2c", title = "What is SVG to Compose?") {
+    DocSection(id = "what-is-svg-to-compose", title = "What is SVG to Compose?") {

Also update the corresponding TocEntry in Faq.kt.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt`
around lines 83 - 98, The section ID in WhatIsS2cSection should match the FAQ
question slug: change DocSection(id = "what-is-s2c", ...) to id =
"what-is-svg-to-compose" inside the WhatIsS2cSection composable, and then update
the corresponding TocEntry/Toc list in Faq.kt (the entry associated with the
"What is SVG to Compose?" question in faqQuestions/TocEntry) to use the same
"what-is-svg-to-compose" fragment so the TOC and section IDs remain consistent
for SEO and URL fragments.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt (1)

146-155: Consider extracting the Regex to a constant for better performance.

The Regex("[\\u0000-\\u001F]") is instantiated on every call to escapeJsonString(). If this function is called frequently, compiling the regex repeatedly adds overhead.

♻️ Proposed refactor
+private val CONTROL_CHAR_REGEX = Regex("[\\u0000-\\u001F]")
+
 internal fun String.escapeJsonString(): String = replace("\\", "\\\\")
     .replace("\"", "\\\"")
     .replace("\n", "\\n")
     .replace("\r", "\\r")
     .replace("\t", "\\t")
     .replace("\u000C", "\\f")
     .replace("\u0008", "\\b")
-    .replace(Regex("[\\u0000-\\u001F]")) { match ->
+    .replace(CONTROL_CHAR_REGEX) { match ->
         "\\u${match.value[0].code.toString(16).padStart(4, '0')}"
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt`
around lines 146 - 155, The Regex("[\\u0000-\\u001F]") used inside
String.escapeJsonString() is allocated on every call causing unnecessary
overhead; extract it as a private top-level constant (e.g., PRIVATE val
CONTROL_CHAR_REGEX = Regex("[\\u0000-\\u001F]")) and then use that constant in
escapeJsonString() to avoid recompiling the pattern each invocation while
keeping the replacement logic intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/deploy-website.yml:
- Around line 107-125: The workflow currently checks out the triggering ref via
actions/checkout and later does git push origin HEAD:main, which can push an
arbitrary branch when run with workflow_dispatch; update the job to explicitly
checkout main (use the actions/checkout step to fetch and checkout the main
branch) before modifying app.properties and pushing, and replace pushing HEAD
with pushing the explicit main branch (or push the local main ref) to avoid
accidental branch push; also add a guard condition around the job (or the
LAST_MODIFIED commit step) to skip execution for preview refs or non-production
triggers by checking the triggering ref or an input (e.g., ensure
workflow_dispatch input or GITHUB_REF equals refs/heads/main) so the metadata
update runs only for production builds.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`:
- Around line 219-220: DocNavDropdown is trying to reassign the immutable
parameter isOpen inside the onKeyDown handler; instead, add an onClose: () ->
Unit lambda in DocNavDropdown and pass it into DropdownPanel, then call
handleMenuKeyDown(event, onClose) (or pass the lambda as the second arg) so the
key handler invokes the provided onClose callback to set isOpen = false in the
parent scope; update the DropdownPanel invocation and handleMenuKeyDown call
sites to accept and forward the onClose lambda accordingly.

---

Nitpick comments:
In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt`:
- Around line 146-155: The Regex("[\\u0000-\\u001F]") used inside
String.escapeJsonString() is allocated on every call causing unnecessary
overhead; extract it as a private top-level constant (e.g., PRIVATE val
CONTROL_CHAR_REGEX = Regex("[\\u0000-\\u001F]")) and then use that constant in
escapeJsonString() to avoid recompiling the pattern each invocation while
keeping the replacement logic intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a42bf64-7cfa-4a27-ae98-7088d43ae9a7

📥 Commits

Reviewing files that changed from the base of the PR and between a49ab7b and 700d275.

📒 Files selected for processing (13)
  • .github/actions/configure-bot/action.yml
  • .github/workflows/deploy-website.yml
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/LogoAndDescription.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/CliDocsContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/Index.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Cli.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Faq.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/GradlePlugin.kt
✅ Files skipped from review due to trivial changes (4)
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/LogoAndDescription.kt
  • .github/actions/configure-bot/action.yml
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/CliDocsContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/GradlePlugin.kt
🚧 Files skipped from review as they are similar to previous changes (5)
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Faq.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/Index.kt

- Fix configure-bot action description to match implementation
- Add missing 'export' job dependency in deploy workflow
- Add Escape key handler to dropdown menu panel for a11y
- Extract menu keyboard navigation to reduce complexity
- Replace fully qualified DOM references with imports
- Make footer logo image decorative (empty alt)
- Use SEO-friendly FAQ section ID 'what-is-svg-to-compose'
- Fix formatting and import ordering across website modules
@rafaeltonholo rafaeltonholo force-pushed the chore/seo-geo-improvements branch from 700d275 to 58db23d Compare April 3, 2026 03:45
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt (1)

238-264: Keyboard navigation properly addresses the Escape key feedback.

The implementation correctly handles ArrowUp/ArrowDown (with wrapping), Home/End, and Escape with focus restoration to the trigger.

Minor observation: The Home/End handlers rely on safe casts to handle empty items, while ArrowDown/ArrowUp delegate to focusAdjacentMenuItem which has an explicit early return. Both approaches are safe, but an early return at the top would be slightly more consistent and explicit.

♻️ Optional: Add early return for consistency
 private fun handleMenuKeyDown(event: KeyboardEvent, closeMenu: () -> Unit) {
     val container = event.currentTarget as? Element ?: return
     val items = container.querySelectorAll("[role='menuitem']")
+    if (items.length == 0) return
     when (event.key) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`
around lines 238 - 264, The handlers in handleMenuKeyDown rely on safe casts for
Home/End but other branches use focusAdjacentMenuItem's early return; make
behavior consistent by adding an explicit early return when items.length == 0 at
the top of handleMenuKeyDown (right after obtaining items) so all key branches
(ArrowUp/ArrowDown, Home, End, Escape) assume non-empty items; update references
to items and existing casts in handleMenuKeyDown accordingly and keep
focusAdjacentMenuItem unchanged.
.github/workflows/deploy-website.yml (1)

69-75: Prefer passing LAST_MODIFIED into Gradle instead of rewriting app.properties here.

website/site/build.gradle.kts:13-15 and 135-140 read this value during configuration, so this step currently works by mutating repo state and treating that as an implicit task input. Passing the date as a Gradle property/env var would make generateSitemap deterministic and keep the repo write-back isolated to the later job.

♻️ Suggested direction
-          sed -i "s/^LAST_MODIFIED=.*/LAST_MODIFIED=$TODAY/" app.properties
-          cd website && ./gradlew :site:generateSitemap
+          cd website && ./gradlew :site:generateSitemap -PlastModified="$TODAY"
val lastModified = providers.gradleProperty("lastModified")
    .orElse(providers.provider {
        appProperties["LAST_MODIFIED"]?.toString()
            ?: error("LAST_MODIFIED not set in app.properties")
    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy-website.yml around lines 69 - 75, The workflow step
currently mutates app.properties; instead, keep TODAY and the GITHUB_OUTPUT but
stop rewriting the repo and pass the date into Gradle as a property so the
generateSitemap task reads it deterministically; specifically, remove the sed
rewrite of LAST_MODIFIED and invoke the site generate task with a Gradle project
property matching the build script's providers.gradleProperty("lastModified")
(so pass lastModified=$TODAY to ./gradlew :site:generateSitemap) so the site
task (generateSitemap) uses the supplied value instead of modifying
app.properties.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/deploy-website.yml:
- Around line 69-75: The workflow step currently mutates app.properties;
instead, keep TODAY and the GITHUB_OUTPUT but stop rewriting the repo and pass
the date into Gradle as a property so the generateSitemap task reads it
deterministically; specifically, remove the sed rewrite of LAST_MODIFIED and
invoke the site generate task with a Gradle project property matching the build
script's providers.gradleProperty("lastModified") (so pass lastModified=$TODAY
to ./gradlew :site:generateSitemap) so the site task (generateSitemap) uses the
supplied value instead of modifying app.properties.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`:
- Around line 238-264: The handlers in handleMenuKeyDown rely on safe casts for
Home/End but other branches use focusAdjacentMenuItem's early return; make
behavior consistent by adding an explicit early return when items.length == 0 at
the top of handleMenuKeyDown (right after obtaining items) so all key branches
(ArrowUp/ArrowDown, Home, End, Escape) assume non-empty items; update references
to items and existing casts in handleMenuKeyDown accordingly and keep
focusAdjacentMenuItem unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a9a2231c-2131-4840-a5c9-adc21745a63f

📥 Commits

Reviewing files that changed from the base of the PR and between 700d275 and 58db23d.

📒 Files selected for processing (13)
  • .github/actions/configure-bot/action.yml
  • .github/workflows/deploy-website.yml
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/LogoAndDescription.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/CliDocsContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/Index.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Cli.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Faq.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/GradlePlugin.kt
✅ Files skipped from review due to trivial changes (3)
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/LogoAndDescription.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/CliDocsContent.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/AlternativesContent.kt
🚧 Files skipped from review as they are similar to previous changes (7)
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Faq.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/GradlePlugin.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/docs/Cli.kt
  • .github/actions/configure-bot/action.yml
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/pages/Index.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/docs/FaqContent.kt

Replace CSS ::before pseudo-element separators with inline SpanText
middle-dot separators. Pseudo-elements appear at the start of wrapped
lines; inline text wraps naturally with the content.
The attrsModifier onKeyDown callback provides SyntheticKeyboardEvent,
not raw KeyboardEvent. Use nativeEvent.currentTarget to access the
DOM element.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt (1)

257-262: Consider storing a direct reference to the trigger element.

The current approach of navigating via container.parentElement?.querySelector("[role='button']") relies on the DOM structure remaining stable. While this works with the current implementation, storing a ref to the trigger element would be more robust against future refactoring.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`
around lines 257 - 262, The Escape handler uses
container.parentElement?.querySelector("[role='button']") to find the trigger
which is brittle; change the component to store a direct reference to the
trigger element (e.g., a property like triggerButtonRef or triggerElement) when
rendering the trigger button and update the Escape branch to call focus() on
that ref instead of querying the DOM; ensure the ref is assigned/cleared
alongside open/close logic (refer to container, closeMenu(), and the Escape
handler) so focus restoration works even if DOM structure changes.
website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt (1)

146-157: Add regression tests around escapeJsonString().

This helper now protects every interpolated JSON-LD field, and website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/SeoHead.kt:116-148 writes its output straight into the <script type="application/ld+json"> node. A small test matrix for quotes, backslashes, newlines, and raw U+0000..U+001F characters would make this much safer to evolve.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt`
around lines 146 - 157, Add regression tests for String.escapeJsonString() (and
its CONTROL_CHAR_REGEX behavior) that cover a matrix of inputs: double quotes,
single quotes, backslashes, newlines (\n and \r), tabs, formfeed, backspace, and
representative control characters from U+0000 to U+001F (including U+0000,
U+0001, U+0008, U+000C, U+001F). For each case assert the escaped output matches
the expected JSON-escaped form (e.g., backslash-escaped characters and \\uXXXX
for control chars) and include a round-trip check that embedding the escaped
value into a minimal JSON object string used by SeoHead (the JSON-LD script
output path) can be parsed by a JSON parser without error. Ensure tests exercise
multi-character strings with mixed cases (quotes + control chars) and fail if
any control char is not escaped by CONTROL_CHAR_REGEX or escapeJsonString().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt`:
- Around line 45-47: The long JSON "description" line in StructuredData.kt
exceeds the 120-char limit; extract that description text into a top-level
constant (e.g., APP_DESCRIPTION or SITE_DESCRIPTION) and replace the inline
literal in the raw JSON string with the constant interpolation (using the same
escapeJsonString() where needed). Update the raw string in the StructuredData
component to reference the new constant so the line length is reduced and future
edits only require changing the single constant rather than the embedded JSON.

---

Nitpick comments:
In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt`:
- Around line 146-157: Add regression tests for String.escapeJsonString() (and
its CONTROL_CHAR_REGEX behavior) that cover a matrix of inputs: double quotes,
single quotes, backslashes, newlines (\n and \r), tabs, formfeed, backspace, and
representative control characters from U+0000 to U+001F (including U+0000,
U+0001, U+0008, U+000C, U+001F). For each case assert the escaped output matches
the expected JSON-escaped form (e.g., backslash-escaped characters and \\uXXXX
for control chars) and include a round-trip check that embedding the escaped
value into a minimal JSON object string used by SeoHead (the JSON-LD script
output path) can be parsed by a JSON parser without error. Ensure tests exercise
multi-character strings with mixed cases (quotes + control chars) and fail if
any control char is not escaped by CONTROL_CHAR_REGEX or escapeJsonString().

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt`:
- Around line 257-262: The Escape handler uses
container.parentElement?.querySelector("[role='button']") to find the trigger
which is brittle; change the component to store a direct reference to the
trigger element (e.g., a property like triggerButtonRef or triggerElement) when
rendering the trigger button and update the Escape branch to call focus() on
that ref instead of querying the DOM; ensure the ref is assigned/cleared
alongside open/close logic (refer to container, closeMenu(), and the Escape
handler) so focus restoration works even if DOM structure changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 10a58fa8-0d51-4d7b-b121-2da6a75254f5

📥 Commits

Reviewing files that changed from the base of the PR and between 58db23d and 54687b3.

📒 Files selected for processing (4)
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/DocNavDropdown.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/molecules/footer/Links.kt
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/footer/Footer.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/organisms/footer/Footer.kt

Comment on lines +45 to +47
| "softwareVersion": "${BuildConfig.VERSION.escapeJsonString()}",
| "url": "${BASE_URL.escapeJsonString()}",
| "description": "Convert SVG and Android Vector Drawables into Jetpack Compose ImageVector code. Available as a CLI tool, Gradle plugin, and Kotlin Multiplatform library.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Break the long description out of this raw string.

Line 47 is over the repository's 120-character Kotlin limit. Extracting that copy to a constant also makes future blurb updates less painful.

♻️ Proposed change
+private const val SOFTWARE_APPLICATION_DESCRIPTION =
+    "Convert SVG and Android Vector Drawables into Jetpack Compose ImageVector code. " +
+        "Available as a CLI tool, Gradle plugin, and Kotlin Multiplatform library."
+
 data object SoftwareApplicationStructuredData : StructuredDataType {
     // language=json
     override fun toJsonLd(): String = """
         |{
         |  "@context": "https://schema.org",
@@
-        |  "description": "Convert SVG and Android Vector Drawables into Jetpack Compose ImageVector code. Available as a CLI tool, Gradle plugin, and Kotlin Multiplatform library.",
+        |  "description": "${SOFTWARE_APPLICATION_DESCRIPTION.escapeJsonString()}",
         |  "downloadUrl": "https://github.com/rafaeltonholo/svg-to-compose",
         |  "license": "https://opensource.org/licenses/MIT"
         |}
     """.trimMargin()
 }

As per coding guidelines, **/*.kt: Maximum line length: 120 characters.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@website/site/src/jsMain/kotlin/dev/tonholo/s2c/website/components/atoms/StructuredData.kt`
around lines 45 - 47, The long JSON "description" line in StructuredData.kt
exceeds the 120-char limit; extract that description text into a top-level
constant (e.g., APP_DESCRIPTION or SITE_DESCRIPTION) and replace the inline
literal in the raw JSON string with the constant interpolation (using the same
escapeJsonString() where needed). Update the raw string in the StructuredData
component to reference the new constant so the line length is reduced and future
edits only require changing the single constant rather than the embedded JSON.

@rafaeltonholo rafaeltonholo merged commit 672e48c into main Apr 3, 2026
4 checks passed
@rafaeltonholo rafaeltonholo deleted the chore/seo-geo-improvements branch April 3, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant