Skip to content

feat: add GitHub alert banner component#603

Open
mikepenz wants to merge 2 commits into
developfrom
feat/github-alerts
Open

feat: add GitHub alert banner component#603
mikepenz wants to merge 2 commits into
developfrom
feat/github-alerts

Conversation

@mikepenz

@mikepenz mikepenz commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Renders GitHub alertsNOTE, TIP, IMPORTANT, WARNING, CAUTION — as an accent bar with a tinted icon and title, followed by the alert's block content.

The GFM parser has emitted GFMElementTypes.ALERT nodes since markdown 0.7.5, but they fell through to the else branch of MarkdownElementInternal, so the [!NOTE] title was swallowed and the body rendered as bare children.

Changes

Follows the existing component architecture:

  • MarkdownAlertType — the five markers, resolved case-insensitively from the ALERT_TITLE token.
  • MarkdownAlert — mirrors MarkdownBlockQuote's bar drawing and child dispatch.
  • MarkdownComponents.alert + ALERT -> dispatch, so it is overridable like every other element.
  • MarkdownAlertColors / MarkdownAlertDimens / MarkdownAlertPadding — grouped configuration models, each with the usual interface + private Default* + factory shape.
  • MarkdownTypography.alertTitle styles the title, MarkdownA11yLabels.alert labels the container.
  • Icons are ImageVectors built via addPathNodes, so the core module keeps its compileOnly runtime/ui/foundation dependency set, and are swappable through LocalMarkdownAlertIcons.
Markdown(
    markdown,
    colors = markdownColor(alert = markdownAlertColors(note = Color.Blue)),
    dimens = markdownDimens(alert = markdownAlertDimens(barThickness = 2.dp)),
    padding = markdownPadding(alert = markdownAlertPadding(iconSpacing = 12.dp)),
)

Theming

Material's color schemes carry no slot with the semantics these alerts rely on — a green "tip", an amber "warning" — so MarkdownAlertColorDefaults ships GitHub's light and dark palettes rather than deriving them from the theme. m2 selects between them via MaterialTheme.colors.isLight; m3 has no such flag, so it derives darkness from colorScheme.background.luminance(). Every color stays overridable.

One subtlety worth reviewing

The > marker the parser leaves in the tree is a MarkdownTokenTypes.BLOCK_QUOTE, while a nested blockquote inside an alert is a MarkdownElementTypes.BLOCK_QUOTE. Same name, different type. My first pass skipped the element type, which silently dropped nested quotes. MarkdownAlert now skips only the marker token, and AlertAstShapeTest.nestedBlockQuoteIsAnElementNotAMarkerToken pins the distinction.

Breaking change

MarkdownColors, MarkdownDimens, MarkdownPadding, MarkdownTypography and MarkdownComponents each gained one member. Direct implementers of these interfaces must add the new override. Consumers of the markdownColor() / markdownDimens() / markdownPadding() / markdownTypography() / markdownComponents() factories are unaffected — the new parameters are defaulted. .api files regenerated via apiDump; the diff is signature-arity changes only, no member removals.

Testing

  • AlertAstShapeTest (7 tests) pins the AST shape the component depends on.
  • 44 Paparazzi snapshots across m2/m3 in light and dark: each alert type, lowercase markers, multi-paragraph bodies, headings/lists/links inside alerts, nested quotes, and an unrecognised marker degrading to a plain blockquote. No pre-existing snapshot changed.
  • ./gradlew apiCheck :multiplatform-markdown-renderer:jvmTest :sample:android:verifyPaparazziDebug passes.
  • Alerts added to sample.md, so all sample apps exercise them.

Not included

CHANGELOG.md is a one-line pointer to the GitHub releases page and has been untouched since the initial commit, so I left it alone — these notes are meant for the release entry instead.

mikepenz added 2 commits July 9, 2026 21:48
- org.jetbrains:markdown 0.7.5 -> 0.7.6
Render GFM alerts (`> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]`,
`> [!CAUTION]`) as an accent bar with a tinted icon and title, followed by the
alert's block content.

The parser has emitted `GFMElementTypes.ALERT` nodes since markdown 0.7.5, but
they fell through to the `else` branch of `MarkdownElementInternal`, so the
title was swallowed and the body rendered as bare children.

- `MarkdownAlertType` resolves the marker from the `ALERT_TITLE` token,
  case-insensitively
- `MarkdownAlert` mirrors `MarkdownBlockQuote`'s bar drawing and child dispatch
- `MarkdownComponents.alert` + `ALERT ->` dispatch, overridable like every
  other element
- `MarkdownAlertColors`, `MarkdownAlertDimens` and `MarkdownAlertPadding` group
  the alert configuration, reached via `markdownColor(alert = ...)`,
  `markdownDimens(alert = ...)` and `markdownPadding(alert = ...)`
- `MarkdownTypography.alertTitle` styles the title; `MarkdownA11yLabels.alert`
  labels the container
- Icons ship as `ImageVector`s built with `addPathNodes`, keeping the core
  module free of a material dependency, and are swappable through
  `LocalMarkdownAlertIcons`

Material's color schemes carry no slot with the semantics alerts rely on (a
green "tip", an amber "warning"), so `MarkdownAlertColorDefaults` ships GitHub's
light and dark palettes. m2 selects between them via `MaterialTheme.colors.isLight`,
m3 derives it from `colorScheme.background.luminance()`.

Note the `> ` marker is a `MarkdownTokenTypes.BLOCK_QUOTE` while a nested quote
inside an alert is a `MarkdownElementTypes.BLOCK_QUOTE`; only the former is
skipped, so nested quotes still render.

BREAKING CHANGE: `MarkdownColors`, `MarkdownDimens`, `MarkdownPadding`,
`MarkdownTypography` and `MarkdownComponents` each gained a member. Direct
implementers of these interfaces must add the new override; consumers of
`markdownColor()`, `markdownDimens()`, `markdownPadding()`,
`markdownTypography()` and `markdownComponents()` are unaffected.

Adds `AlertAstShapeTest` pinning the AST shape the component depends on, and 44
Paparazzi snapshots across m2/m3 in light and dark.
Copilot AI review requested due to automatic review settings July 9, 2026 19:50

Copilot AI left a comment

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.

Pull request overview

Adds first-class rendering for GitHub-flavoured “alerts” (> [!NOTE], > [!WARNING], etc.) by introducing a dedicated MarkdownAlert element, theming/config models (colors/dimens/padding/typography), and component dispatch so alerts are overridable like other markdown elements. This closes the gap where GFMElementTypes.ALERT nodes were previously falling through and losing the alert title.

Changes:

  • Introduces alert domain + rendering: MarkdownAlertType, MarkdownAlert, alert icons/colors/dimens/padding, and LocalMarkdownAlertIcons.
  • Wires alert dispatch through MarkdownComponents.alert and MarkdownElementInternal (ALERT -> ...), plus a helper to resolve the alert type from the AST.
  • Adds comprehensive tests/snapshots + documentation updates, and bumps the org.jetbrains:markdown version to 0.7.6 with regenerated API dumps.

Reviewed changes

Copilot reviewed 31 out of 75 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
sample/shared/src/commonMain/composeResources/files/sample.md Adds alert examples to the sample markdown content.
sample/android/src/main/kotlin/com/mikepenz/markdown/ui/m3/AlertsTests.kt Adds m3 Paparazzi preview coverage for alert variants and edge cases.
sample/android/src/main/kotlin/com/mikepenz/markdown/ui/m2/AlertsTests.kt Adds m2 Paparazzi preview coverage for alert variants and edge cases.
README.md Documents alert support, theming knobs, and icon overriding.
multiplatform-markdown-renderer/src/commonTest/kotlin/com/mikepenz/markdown/parser/AlertAstShapeTest.kt Pins the AST shape emitted for alerts, including nested blockquote handling.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/utils/Extensions.kt Adds ASTNode.findAlertType(...) helper to resolve an alert type from ALERT_TITLE.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownTypography.kt Extends typography with alertTitle styling.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownPadding.kt Extends padding model with alert: MarkdownAlertPadding.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownDimens.kt Extends dimens model with alert: MarkdownAlertDimens.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownColors.kt Extends colors model with alert: MarkdownAlertColors.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownAlertType.kt Adds enum for the 5 GitHub alert markers with parsing helper.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownAlertPadding.kt Adds alert padding model + factory.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownAlertIcons.kt Adds alert icon model, defaults, and icon factories (no material-icons dependency).
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownAlertDimens.kt Adds alert dimens model + factory.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownAlertColors.kt Adds alert color model + GitHub light/dark defaults + factory.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/model/MarkdownA11yLabels.kt Adds a11y label generator for alert containers.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/compose/MarkdownExtension.kt Adds ALERT -> components.alert(...) dispatch.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/compose/elements/MarkdownAlert.kt Implements the new alert rendering element (bar + icon/title + block content).
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/compose/ComposeLocal.kt Adds LocalMarkdownAlertIcons composition local.
multiplatform-markdown-renderer/src/commonMain/kotlin/com/mikepenz/markdown/compose/components/MarkdownComponents.kt Adds alert component slot + default implementation using MarkdownAlert.
multiplatform-markdown-renderer/api/multiplatform-markdown-renderer.klib.api Updates public API dump for new models/functions and signature changes.
multiplatform-markdown-renderer/api/jvm/multiplatform-markdown-renderer.api Updates JVM API dump for new alert APIs and signature changes.
multiplatform-markdown-renderer-m3/src/commonMain/kotlin/com/mikepenz/markdown/m3/MarkdownTypography.kt Adds alertTitle default styling for m3.
multiplatform-markdown-renderer-m3/src/commonMain/kotlin/com/mikepenz/markdown/m3/MarkdownColors.kt Adds alert colors to m3, deriving dark/light from background luminance.
multiplatform-markdown-renderer-m3/api/multiplatform-markdown-renderer-m3.klib.api Updates m3 klib API dump for function signature changes.
multiplatform-markdown-renderer-m3/api/jvm/multiplatform-markdown-renderer-m3.api Updates m3 JVM API dump for function signature changes.
multiplatform-markdown-renderer-m2/src/commonMain/kotlin/com/mikepenz/markdown/m2/MarkdownTypography.kt Adds alertTitle default styling for m2.
multiplatform-markdown-renderer-m2/src/commonMain/kotlin/com/mikepenz/markdown/m2/MarkdownColors.kt Adds alert colors to m2, selecting default palette via MaterialTheme.colors.isLight.
multiplatform-markdown-renderer-m2/api/multiplatform-markdown-renderer-m2.klib.api Updates m2 klib API dump for function signature changes.
multiplatform-markdown-renderer-m2/api/jvm/multiplatform-markdown-renderer-m2.api Updates m2 JVM API dump for function signature changes.
gradle/libs.versions.toml Bumps org.jetbrains:markdown to 0.7.6.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

!pastTitle -> if (child.type == GFMTokenTypes.ALERT_TITLE) pastTitle = true
child.type == EOL || child.type == WHITE_SPACE || child.type == BLOCK_QUOTE -> Unit
else -> {
if (seenContent) Spacer(Modifier.height(padding.titleSpacing))
modifier = Modifier
.semantics { contentDescription = a11yLabels.alert(title) }
.drawBehind {
val x = bar.calculateStartPadding(LayoutDirection.Ltr).toPx()
Comment on lines +16 to +17
/** Padding above and below the alert's block content */
val content: PaddingValues
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants