Context
Original task description
As a possible solution to T159738 I am exploring porting QuickSurveys to Vue.js
One issue I ran into is that for external surveys, QuickSurveys renders a button that is a link to a URL that is opened in a new window via target=_blank
Ideally, I'd like to be able to signal by passing in an href that this should be a link not a button.
Considerations
In Codex we recommend using the <button> element for buttons and the Button component always renders a <button> element, not supporting links. However, there are link buttons in use, and not supporting this use case at all will limit migration. In particular, link buttons are used throughout Vector to support no-JS contexts:
While we don't want to encourage most uses of link buttons in the design system, there are things we can do to enable developer users of Codex to apply Codex Button styles to other elements.
Potential solutions
1. No support
The first option is the status quo, which is not to provide any support for styling links as Codex buttons.
The consequences of this would be either:
- Vector cannot be migrated to Codex, or
- Most of the Codex button styles would need to be copied from Codex and duplicated in Vector. Every time the Codex Button styles are updated, the same update would need to be made in Vector. You might think that the button styles are simple, but they are not.
2. Wrapped button hack
One way to get this to work immediately would be to wrap a button in a link, like so:
<a href="example.com"> <button class="cdx-button cdx-button--action-progressive cdx-button--weight-primary"> Button text </button> </a>
This creates a link that also shows all of the appropriate Codex button styles. However, there are 2 major issues:
- This is probably not acceptable from an accessibility standpoint in terms of usability with assistive technology or keyboard navigation
- This would mean a markup change across much of Vector
3. CSS-only support
Currently, if you apply Codex Button classes to an inline element like <a> or <label> (like in the checkbox hack), there are some issues:
- Many styles are set inside either an :enabled or a disabled selector. Since non-button elements like <a> or <label> don't have enabled/disabled states, these styles don't get applied
- The inline elements don't have the right height, and setting display: inline-block: or display: inline-flex` requires some additional styles to get the vertical alignment right
To fix this, we could do the following:
- In Codex, use :not( :disabled ) selectors instead of :enabled, which will allow enabled styles to apply to other elements. Disabled styles aren't needed at the moment, so we don't need to provide a path for that.
- In the feature using these buttons, other styles will need to be applied to get the size and spacing right
This works well and enables us to quietly support styling inline elements as buttons, but is a partial solution that could be confusing to use and adds specificity to the button styles.
3.1 Use CSS classes instead
Instead of :not( :disabled ), we could add CSS classes for non-button buttons. This would also enable us to add the extra styles needed to fully style links as buttons:
.cdx-button { &:enabled, &--fake-button--enabled { // Enabled button styles } &:disabled, &--fake-button--disabled { // Disabled button styles } &--fake-button { // Special styles needed for other elements } }
4. Full support
Full support of link buttons (and other elements; we also need to provide a way to style labels as buttons) would entail:
- Updating the Button Vue component to take in props to enable this. We could do this in a few different ways (allowing the user to provide the element they want to render, adding an href prop, etc)
- Providing new CSS classes for CSS-only implementation
- Documenting this on the docs site
This would be a more complete solution and would enable us to have greater control over use of link and label buttons in the design system, but may encourage unwanted use of link buttons which continue to be an a11y antipattern.
Acceptance criteria
- Determine an implementation path - we have decided to go with the CSS-only support solution and will determine if we want to do :not( :enabled ) or extra CSS classes as part of code review
- Document the decision
- Complete the implementation, if applicable