Skip to content

Add S2 button whitelist with authorable outline variant - #675

Open
echen-adobe wants to merge 18 commits into
stagefrom
milo-buttons
Open

Add S2 button whitelist with authorable outline variant#675
echen-adobe wants to merge 18 commits into
stagefrom
milo-buttons

Conversation

@echen-adobe

@echen-adobe echen-adobe commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces Spectrum 2 (S2) button styling under an opt-in .s2 namespace, gated by a central block whitelist in utils.js/scripts.js. Adds an authorable outline variant scoped to S2. Adopts the design-reviewed focus ring for black-fill buttons (light and dark sections). Cleans up scoped button overrides in transparent-img-marquee, comparison-table-v2, and blog-columns now that styles are managed centrally.


Jira Ticket

Resolves: MWPW-202637


Test URLs

Env URL
Before https://main--da-express-milo--adobecom.aem.page/docs/library/kitchen-sink/ax-columns
After https://milo-buttons--da-express-milo--adobecom.aem.page/drafts/echen/s2-buttons-transparent-img-marquee
After https://milo-buttons--da-express-milo--adobecom.aem.page/drafts/echen/s2-buttons-comparison-table-v2
After https://milo-buttons--da-express-milo--adobecom.aem.page/drafts/echen/s2-buttons-ax-columns
After https://milo-buttons--da-express-milo--adobecom.aem.page/drafts/echen/s2-buttons-blog-columns

Verification Steps

  • Open the Before URL and note button appearance (colors, focus ring, outline variant).
  • Open the After URL and verify S2 buttons render with correct fill, outline, and focus ring styles under .s2.
  • Confirm the outline variant is authorable via the #_button-outline href hash.
  • Confirm black-fill button focus ring renders correctly on both light and dark sections.
  • Verify blocks NOT in the whitelist are unaffected by S2 button styles.
  • For the AX Columns the examples are at the bottom of the kitchen sink page

Potential Regressions

| After | https://milo-buttons--da-express-milo--adobecom.aem.page/docs/library/kitchen-sink/ax-columns?martech=off |
| After | https://milo-buttons--da-express-milo--adobecom.aem.page/docs/library/kitchen-sink/comparison-table-v2 |
| After | https://milo-buttons--da-express-milo--adobecom.aem.page/docs/library/kitchen-sink/transparent-img-marquee |
| After | https://milo-buttons--da-express-milo--adobecom.aem.page/docs/library/kitchen-sink/blog-columns


Additional Notes

CSS lint flagged ~20 translucent color values in the S2 outline button rules (e.g. #00000038, #ffffff38, #000000ed). These are intentional alpha-channel design tokens for outline button states (border, hover, active, focus) and cannot be replaced with solid var(--color-black) / var(--color-white) tokens without breaking the visual. Proper S2 translucent token definitions are pending upstream. min-height: 40px on .con-button in transparent-img-marquee.css is likewise intentional (40px pill height per design spec) and cannot use var(--spacing-*) per project policy.

echen-adobe and others added 6 commits August 5, 2026 14:32
The dark .con-button.fill/.button.fill focus ring used a #292929 inner ring that is invisible on a dark section. Use the SoT focus indicator (--color-focus-ring-strong #4B75FF) as the inner ring hugging the white button, with a white outer ring against the dark background, mirroring the light-mode rule and Express's existing .button.dark:focus convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the black-fill button's double box-shadow focus ring with the QA-approved S2 SoT 'Focus Ring Background' treatment already shipped in transparent-img-marquee: a 2px --color-focus-ring-strong outline offset 2px, with an 11%-white halo. Same ring in light and dark mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Instead of overriding the global (legacy-matched) con-button rules and risking site-wide regressions, introduce an opt-in 's2' namespace. A block/section marked 's2' renders the design-reviewed Spectrum-2 CTAs: accent blue #3b63fb, black fill (#292929), the QA-approved outline ladder and focus ring, and dark-section flips — covering both .con-button.{blue,outline,fill} and the legacy .button.{accent,fill} the fallback emits (so ax-columns benefits). Everything is scoped under .s2, so existing buttons are untouched. The black fill/focus previously added globally now live under this namespace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add S2_BUTTON_BLOCKS in decorateAreaWithLCP (runs on every page/fragment) to apply the s2 class to whitelisted blocks centrally, so the Spectrum-2 button styling rolls out block-by-block without per-page authoring. Starts with ax-columns; add blocks as they are verified, then retire the gate for a global default. Manual s2 opt-in still works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rquee, comparison-table-v2, blog-columns

- Make the S2 outline (secondary) button link-authorable via a #_button-outline hash: generalize the decorateButtonsDeprecated fallback to handle any #_button-<class> (fill/outline), matching milo, and extend the .s2 outline rules to cover legacy .button.outline via :is(.con-button, .button).
- Add transparent-img-marquee, comparison-table-v2, blog-columns to S2_BUTTON_BLOCKS and remove their now-redundant local button rules (the .s2 outline/focus were ported verbatim from transparent-img-marquee; the two fill blocks' local .con-button.fill now come from .s2). Blue shifts from legacy #5c5ce0 to SoT #3b63fb on these blocks — intended S2 alignment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aem-code-sync

aem-code-sync Bot commented Aug 6, 2026

Copy link
Copy Markdown

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch and validate page speed.
In case there are problems, just click a checkbox below to rerun the respective action.

  • Re-run all PSI checks
  • Re-run failed PSI checks
  • Re-sync branch
Commits

@github-actions github-actions Bot added the Ready for Review Ready for peer review. label Aug 6, 2026
@echen-adobe
echen-adobe marked this pull request as draft August 10, 2026 16:54
@github-actions github-actions Bot removed the Ready for Review Ready for peer review. label Aug 10, 2026
@echen-adobe echen-adobe added the Ready for Review Ready for peer review. label Aug 10, 2026
@echen-adobe
echen-adobe marked this pull request as ready for review August 10, 2026 20:51
@meganthecoder

Copy link
Copy Markdown
Contributor

@echen-adobe Has this gone through design review yet? Because of the nature of the request, I think they should sign off on it.

@meganthecoder meganthecoder 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.

A few initial comments, and then as I posted earlier, I think this should be reviewed by design first.

Comment on lines +398 to +402
// Milo's core loadArea() never calls config.decorateArea for the main
// document — only its fragment block does, for fragment content (see
// libs/blocks/fragment/fragment.js). Blocks authored directly on the page
// (the common case) are classed explicitly after loadArea() in loadPage().
applyS2ButtonClasses(area);

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.

Yes, because the button decoration function is called in the block. It'd be good to check with Ernest that we want to deviate from milo in this way. It sounds like once we retire the gate, buttons would get decorated in the body too.

Comment on lines +367 to +384
// Custom button variants authored the milo way via `#_button-<class>`
// hashes on the href (e.g. `#_button-fill`, `#_button-outline`). The
// blocks that reach this fallback (ax-columns, banner,
// fullscreen-marquee, link-list) bypass milo's decorateButtons, which
// would normally strip these hashes and add the classes — so replicate
// that here: add each class, strip the hash, and drop the default
// `accent` when an explicit fill/outline variant is requested.
// Additive and hash-gated: no effect on content without these hashes.
if ($a.classList.contains('button')) {
const customClasses = [...originalHref.matchAll(/#_button-([a-zA-Z-]+)/g)];
customClasses.forEach(([token, cls]) => {
$a.classList.add(cls);
$a.setAttribute('href', $a.href.replace(token, ''));
});
if ($a.classList.contains('fill') || $a.classList.contains('outline')) {
$a.classList.remove('accent');
}
}

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.

Ideally we would upgrade to the new button decoration rather than continue to add code to the deprecated function. Do you have an idea of how much work that would be?

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.

Why ax-columns stays on decorateButtonsDeprecated (whitelist), not milo's native decorateButtons

We prototyped switching ax-columns to call milo's real decorateButtons directly, matching the pattern already used by blog-columns, comparison-table-v2, transparent-img-marquee, etc. It doesn't hold up for this block:

  • Milo's decorateButtons only converts links matching em a, strong a, or p > a strong (verified against the live decorate.js source) — i.e., a link needs bold/italic formatting to be recognized as a CTA at all.
  • ax-columns content relies on the older Express convention where any link alone in a paragraph becomes a button, formatted or not. That behavior only exists in the deprecated fallback (decorateButtonsDeprecated's own single-child-of-p check) — milo has no equivalent.
  • Switching would silently drop button styling from every plain-link CTA authored in existing ax-columns content, including the #_button-fill/#_button-outline hash convention on unformatted links, since milo never even reaches them to strip the hash. This wouldn't be a CSS specificity fix — the elements never get a class in the first place.
  • Working around it requires re-implementing a plain-link catch-up in ax-columns.js (matching what blog-columns.js already does), which only shrinks — doesn't eliminate — the custom classification code the migration was meant to remove.

Given ax-columns is one of our highest-traffic, highest-regression-risk blocks, the whitelist/deprecated-fallback approach remains the safer choice until/unless we're ready to also handle the plain-link gap deliberately, rather than fold it into a "quick" migration.

https://main--da-express-milo--adobecom.aem.page/drafts/echen/plain-link-decoration-demo


let fragmentLcpPreloaded = false;

// Rollout gate for the Spectrum-2 (s2) button system. Blocks listed here get the

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.

Can you confirm with design that S2 is the right name for our design system? We're not fully on S2 so they might have a name more specific to express.

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.

Its based on the S2 buttons in the design, I think this is a good name for it.

Screenshot 2026-08-18 at 10 47 46 AM

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