Skip to content

feat(link): 2nd-gen template, stories, styling#6326

Open
aramos-adobe wants to merge 1 commit into
mainfrom
aziz/link-files-api-styles
Open

feat(link): 2nd-gen template, stories, styling#6326
aramos-adobe wants to merge 1 commit into
mainfrom
aziz/link-files-api-styles

Conversation

@aramos-adobe
Copy link
Copy Markdown
Contributor

@aramos-adobe aramos-adobe commented May 19, 2026

Description

Implements Phases 2–5 of the Link 2nd-gen migration (washing-machine workflow): setup, public API direction, accessibility policy documentation, and S2 styling. Phase 6 (testing) and full Phase 7 (documentation) are not in scope — Storybook stories, migration guide, and Typography cross-links are included only as a visual validation scaffold and will be refined in a follow-up PR along with dedicated a11y verification and automated tests.

Link is an atypical migration: there is no swc-link custom element and no core package. Delivery is CSS + native <a href>, aligned with migration plan and PR 6304 packaging review.

2nd-gen files added or updated (2nd-gen/packages/):

Stylesheets

  • swc/stylesheets/link.css — S2 link presentation migrated from spectrum-css spectrum-two (components/link/index.css); BEM modifiers (.swc-Link, --secondary, --quiet + --standalone, --staticWhite, --staticBlack, --inline); --swc-* custom properties (no --mod-*)
  • swc/stylesheets/global/global-link.css — opt-in baseline for bare <a> (no cascade layer; imports link.css for modifiers)
  • swc/stylesheets/typography.css — regenerated; default anchor rules for .swc-Typography--prose and .swc-Typography--links via :where(a)
  • tools/swc-tokens/src/typography.js — generator appends prose/links anchor block (no @import of link.css into typography)

Documentation (scaffold — Phase 7 follow-up)

  • swc/components/link/migration-guide.mdxsp-link → native <a>, import paths, attribute→class mapping, a11y policy callouts
  • swc/components/link/stories/link.stories.ts + link.template.ts — Playground + option stories for standalone / prose / link-list contexts (visual review only)
  • swc/components/typography/migration-guide.mdx — links section; cross-link to Link guide
  • swc/components/typography/stories/typography.stories.ts — inline link in Prose story; LinkList story (swc-Typography--links)

Packaging / Storybook

  • swc/package.json — exports for link.css and global-link.css
  • swc/.storybook/preview.ts — imports link.css
  • swc/.storybook/preview-head.html.link-samples layout utilities

Motivation and context

Part of the 1st-gen → 2nd-gen component migration workstream. Epic SWC-1956.

Key architectural decisions in this PR (see migration plan for full rationale):

  • CSS + native <a> only — no transitional sp-link / CE; prose and link lists use Typography wrappers
  • link.css holds explicit BEM modifiers; typography.css (generator) supplies default unclassed anchor appearance inside .swc-Typography--prose and .swc-Typography--links with :where(a) so modifier classes can override
  • global-link.css is opt-in for app-wide bare <a> baseline — not bundled in swc.css, intentionally outside cascade layers
  • Quiet requires .swc-Link--quiet.swc-Link--standalone (section-scoped pattern; not for undifferentiated body prose)
  • Inline is internal/CSS-level (inherit in prose); not an author-facing variant like 1st-gen
  • No disabled on navigational links — documented in migration guide (SWC-966)

Related issue(s)

  • Epic SWC-1956
  • Packaging direction from PR 6304 review

Out of scope (follow-up PR)

  • Phase 6: Playwright / Storybook a11y specs, VRT, prose + static-color regression coverage
  • Phase 7 (complete): story JSDoc polish, contributor-docs updates, full manual a11y test documentation
  • Phase 8: status table, changeset (if needed on merge), peer sign-off checklist completion
  • Additive: trailing icon (A3) — Design / React sync

Manual review test cases

Stylesheets

  • Import @adobe/spectrum-wc/link.css and @adobe/spectrum-wc/typography.css; confirm standalone .swc-Link modifiers (default, secondary, static white/black, quiet+standalone) match S2 intent
  • Inside .swc-Typography--prose, unclassed <a href> inherits body typography and shows default link color/underline/focus ring
  • Inside .swc-Typography--links, three list links render with default styling (Typography Link list story and Link Link list story)
  • Optional: import @adobe/spectrum-wc/global-link.css — bare <a> without classes pick up standalone link presentation

Generator / packaging

  • yarn workspace @adobe/spectrum-wc stylesheet:typography regenerates typography.css with the prose/links :where(a) block; no @import of link.css in typography output
  • yarn workspace @adobe/spectrum-wc build emits dist/link.css and dist/global-link.css

Storybook scaffold (visual only)

  • Components → Link → Playground — controls for variant, context (standalone / prose / links), size, quiet, inline
  • Option stories render without console errors; treat copy/JSDoc as draft

Migration docs

  • Link migration guide accurately maps sp-link attributes to BEM classes and Typography import paths
  • Typography migration guide links section does not duplicate the full Link story catalog

Accessibility testing checklist

Full keyboard and screen reader testing is deferred to the Phase 6/7 follow-up PR (native <a> behavior, focus-visible in prose, quiet usage scope). Reviewers can verify policy direction from docs/code:

  • Migration guide states: real <a href> for navigation; no disabled on links; quiet scoped to section patterns; icon-only links need accessible names
  • Prose/links default styles use :focus-visible outline (not deprecated double-underline-only focus from 1st-gen)
  • No sp-link / shadow-DOM focus delegation — activation target is the author’s native anchor

@aramos-adobe aramos-adobe requested a review from a team as a code owner May 19, 2026 21:24
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

⚠️ No Changeset found

Latest commit: bf4bda9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@aramos-adobe aramos-adobe self-assigned this May 19, 2026
@aramos-adobe aramos-adobe added 2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. Component:Link Status:Ready for review PR ready for review or re-review. labels May 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6326

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

Copy link
Copy Markdown
Member

@cdransf cdransf left a comment

Choose a reason for hiding this comment

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

Looks good! Just a few minor things. ✨


@import url("../link.css");

@media (forced-colors: active) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we move this media query block to the bottom of the file? ✨

* @cssprop --swc-link-line-height-cjk - CJK standalone link line height
*/

@media (forced-colors: active) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This one too ✨

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.

Forced color overrides aren't needed, those are the defaults anyway!

Copy link
Copy Markdown
Contributor

@5t3ph 5t3ph left a comment

Choose a reason for hiding this comment

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

Great start here!

The comments I have on link would apply to global-link as well.

}
}

.link-samples {
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.

I was originally going to say drop these in favor of the typography ones but see later comment about removing the wrappers entirely, in which case these can also be removed.

Comment on lines +149 to +165
export const StaticWhite: Story = {
args: {
context: 'standalone',
variant: 'staticWhite',
sampleText: 'white link',
},
tags: ['options'],
};

export const StaticBlack: Story = {
args: {
context: 'standalone',
variant: 'staticBlack',
sampleText: 'black link',
},
tags: ['options'],
};
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.

I know documentation is a separate phase but just to note you can use the staticColorsDemo: true to get the background wrapper, and ultimately put these variants in one story (ex. like progress circle) since that decorator expects a child of each type

},
};

function cls(...parts: Array<string | false | null | undefined>): string {
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.

I see taht I also incorrectly had this in the typography template, but we should use classMap from Lit instead of maintain different versions of our own :)

Comment on lines +20 to +30
* @cssprop --swc-link-text-color - Link text color
* @cssprop --swc-link-text-color-hover - Link text color on hover
* @cssprop --swc-link-text-color-down - Link text color on active
* @cssprop --swc-link-text-color-focus - Link text color on focus-visible
* @cssprop --swc-link-focus-indicator-color - Focus ring color
* @cssprop --swc-link-font-family - Standalone link font family
* @cssprop --swc-link-font-size - Standalone link font size
* @cssprop --swc-link-font-weight - Standalone link font weight
* @cssprop --swc-link-inline-font-weight - Inline link font weight override
* @cssprop --swc-link-line-height - Standalone link line height
* @cssprop --swc-link-line-height-cjk - CJK standalone link line height
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.

These can be removed as they won't be picked up since this isn't a web component.

}

.swc-Link {
--_swc-link-focus-indicator-thickness: token("focus-indicator-thickness");
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.

We have overexposure of custom properties and unnecessary extra private properties. Since these are always light DOM styles, we don't really need to worry about exposing any outside of ones that are useful for the modifiers. Have the agent refer to our custom property guidelines to help resolve.

/* =========================
Links (prose and link lists)
========================= */
.swc-Typography--prose :where(a),
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.

I know these are from the generator, I just marked them up here! :)

[nit] We could simplify this selector:

Suggested change
.swc-Typography--prose :where(a),
:is(.swc-Typography--prose, .swc-Typography--links) :where(a)

};

/** Standalone `.swc-Link` sets its own font-size; prose/links inherit from `swc-Body`. */
const STANDALONE_SIZE_FONT_VARS: Record<LinkSize, string | undefined> = {
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.

I think let's skip "size" as a Playground option and not do this, the complication isn't needed.

export const Playground: Story = {
args: {
variant: 'default',
context: 'standalone',
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.

Across all stories - let's skip standalone unless you're actually demonstrating it in a story since otherwise it makes it seem like you need to have swc-Link swc-Link--standalone for the default appearance, which is incorrect.

* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

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.

I know the patterns from Typography inspired some of this setup, but I think we can simplify and not have the wrappers (like .link-samples / .link-row) at all and just have the bare link, one per demo, plus the use of the static colors decorator I noted in another comment.

background-color: transparent;
cursor: pointer;
outline: none;
transition: color token("animation-duration-100") ease-in-out;
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.

Add this to prevent it stretching in something like a flex context and unexpectedly catching clicks in "blank" space:

Suggested change
transition: color token("animation-duration-100") ease-in-out;
transition: color token("animation-duration-100") ease-in-out;
inline-size: fit-content;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. Component:Link Status:Ready for review PR ready for review or re-review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants