Skip to content

Compose: Add DaxButton components to design system#8172

Open
mikescamell wants to merge 20 commits intodevelopfrom
feature/mike/compose/daxbutton
Open

Compose: Add DaxButton components to design system#8172
mikescamell wants to merge 20 commits intodevelopfrom
feature/mike/compose/daxbutton

Conversation

@mikescamell
Copy link
Copy Markdown
Contributor

@mikescamell mikescamell commented Apr 1, 2026

Task/Issue URL: https://app.asana.com/1/137249556945/project/1202857801505092/task/1211090746680572?focus=true

Description

NOTE: Large button icon has been updated to match new guidelines, so is different size from the View version. See this task.

Adds a complete set of Compose Dax* button components to the Android Design System, replacing the old placeholder buttons. Each variant maps 1:1 to an existing View-system counterpart for visual/behavioral parity during Compose migration.

New button components: DaxPrimaryButton, DaxSecondaryButton, DaxGhostButton, DaxDestructivePrimaryButton, DaxDestructiveGhostButton, DaxDestructiveGhostAltButton, DaxDestructiveGhostSecondaryButton, DaxIconButton

Internal base components: DaxButton and DaxOutlinedButton (not public API)

Theme additions: DuckDuckGoStatusColors.criticalPrimary, icons.disabled color token (light + dark)

Lint rule: NoRawM3ButtonUsageDetector flags direct usage of raw M3 button composables (Button, OutlinedButton, TextButton, IconButton, etc.) outside the design-system module.

Cleanup: Removed old placeholder GhostButton, PrimaryButton, SecondaryButton. Updated DaxPromoBottomSheetDialog to use new components.

Steps to test this PR

Compose buttons in demo screen

  • Open the design system internal demo → Buttons section
  • Verify all Compose button variants render alongside their View counterparts in both light and dark themes
  • Verify enabled/disabled states display correctly for each variant
  • Verify small and large sizes render with correct dimensions

DaxIconButton

  • Verify DaxIconButton renders in the demo screen with native icon colors (no tint applied)

Lint rule

  • Run ./gradlew :lint-rules:testNoRawM3ButtonUsageDetectorTest passes
  • Verify that using raw Button() from androidx.compose.material3 in a non-design-system module triggers a lint warning

DaxPromoBottomSheetDialog

  • Verify DaxPromoBottomSheetDialog previews render correctly with the new button imports

UI changes

Before After
(Old placeholder buttons) (New Dax* button variants with correct ADS styling)

Note

Medium Risk
Introduces new Compose button APIs and theme tokens and adds a lint rule that errors on raw Material3 button usage outside design-system, which could surface new build/lint failures in downstream modules. UI behavior is mostly additive but touches shared theming/color definitions used across Compose components.

Overview
Adds a full set of Compose design-system buttons (DaxPrimaryButton, DaxSecondaryButton, DaxGhostButton, destructive variants, and DaxIconButton) backed by new internal base primitives (DaxButton, DaxOutlinedButton) with standardized sizing, pressed/disabled content coloring, and ripple configuration.

Updates demo/screens and existing Compose UI to use the new buttons: the internal component_buttons screen now renders Compose variants alongside View equivalents, and DialogsFragment/DaxPromoBottomSheetDialog swap placeholder Small*/Large* buttons for Dax* buttons.

Extends Compose theme tokens by adding DuckDuckGoColors.status.criticalPrimary and icons.disabled, and introduces a new lint check NoRawM3ButtonUsageDetector (registered + tested) to block direct androidx.compose.material3 button composables outside the design-system module; removes the old placeholder Compose button implementations.

Reviewed by Cursor Bugbot for commit e02d717. Bugbot is set up for automated code reviews on this repo. Configure here.

mikescamell and others added 17 commits April 1, 2026 09:49
Adds the internal DaxButton composable with two overloads: a custom
content slot and a text overload with optional leadingIconPainter.
Includes DaxButtonIcon, DaxButtonColors, DaxButtonSize, and previews.
Adds the internal DaxOutlinedButton composable that delegates to
DaxButton with a border. Includes leadingIconPainter passthrough
and previews.
Includes DaxPrimaryButtonDefaults object with centralized color tokens.
Includes DaxSecondaryButtonDefaults object with centralized color tokens.
Includes DaxGhostButtonDefaults object with centralized color tokens.
Includes DaxDestructivePrimaryButtonDefaults object with centralized color tokens.
Includes DaxDestructiveGhostButtonDefaults object with centralized color tokens.
Includes DaxGhostAltButtonDefaults object with centralized color tokens.
Includes DaxDestructiveGhostSecondaryButtonDefaults object with centralized color tokens.
Removes old placeholder Compose buttons (GhostButton, PrimaryButton,
SecondaryButton). Updates DialogsFragment and DaxPromoBottomSheetDialog
to use real DaxButton components.
Rewrites ComponentButtonsFragment as a standalone Fragment (matching
ComponentTextInputFragment pattern) with Compose buttons interleaved
alongside View buttons for side-by-side comparison.
Flags direct usage of M3 Button, OutlinedButton, TextButton, IconButton
and other raw Material3 button composables outside the design-system
module. Enforces use of Dax* button variants for design consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mikescamell mikescamell changed the title feat(ads): add Compose Dax button components to design system Compose: Add DaxButton components to design system Apr 1, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@mikescamell mikescamell marked this pull request as ready for review April 10, 2026 17:35
@mikescamell mikescamell requested a review from malmstein as a code owner April 10, 2026 17:35
@mikescamell
Copy link
Copy Markdown
Contributor Author

@GerardPaligot would like to get your thoughts regarding Ripples. Right now we don't match the view version, but it seems difficult in Compose to create an exact match. I'm wondering if it's something we change for the Compose version, using the same colours as "pressed" but letting Material handle all the alpha using it's defaults

Copy link
Copy Markdown
Contributor

@GerardPaligot GerardPaligot left a comment

Choose a reason for hiding this comment

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

@mikescamell The difference is expected and acceptable. View buttons are TextView-based with M2 ripple drawables layered on top, while Compose buttons use a Surface with M3's built-in interaction layer. The ripple rendering pipeline is fundamentally different.

M3 Compose actually implements the Material ripple spec more precisely than the M2 View workaround ever did. Since we're migrating toward Compose, I'd treat the Compose version as the canonical one going forward rather than trying to match the M2 View behavior.

Current approach LGTM, just few suggestions/questions but well done for this component, the implementation reflect our previous discussions/decisions about this component!

@Composable
fun DaxDestructiveGhostAltButton(
text: String,
onClick: () -> Unit,
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.

Suggestion: There is a common exception in compose parameter ordering with callback or api slot to be able to move {} outside the call:

// definition
fun DaxDestructiveGhostAltButton(
  text: String,
  ...
  onClick: () -> Unit
) { ... }

// usage
DaxDestructiveGhostAltButton(text = "My button") {
  // on click
}

Personally, I prefer to use the naming approach but with this kind of definition, you can have the choice of the usage.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@GerardPaligot Do you have any examples of that in the wild? I've always thought of the last lambda to be for including a composable, not for clicking or the like.

interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
CompositionLocalProvider(
LocalMinimumInteractiveComponentSize provides Dp.Unspecified,
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.

Note: RIP accessibility with icon component.. :')

Comment thread android-design-system/design-system/src/main/res/drawable/ic_settings_24.xml Outdated
mikescamell and others added 2 commits April 23, 2026 17:48
- cache DaxButtonDimensions across recompositions via remember(size)
- drop accidentally committed tools:fillColor from ic_settings_24

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the 6dp vertical padding wrapped around the M3 Button and the
associated insetVertical / ButtonInsetVertical plumbing. The visible
surface already renders at the 36dp / 48dp design heights; the inset
was legacy Material parity that inflated the layout footprint to
48/60dp. Without it, Dax buttons now occupy exactly the design-spec
height in their parent layouts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants