Page MenuHomePhabricator

investigation: links as components vs. reusable styles [timebox: 1d]
Closed, ResolvedPublic

Description

We need to decide whether we want to approach links as components or as a reusable style: Initial discussions seem to indicate that instead of components, links should actually be a set of classes derived from a mixin. This is an initial theoretical approach. In case this direction is taken, an ADR would be required.

Criteria to keep in mind when selecting an approach:

  • In case the link is expressed as a set of classes: how would icons be included, positioned and spaced correctly in relation to the link text? (see specs)
  • In both cases: how can we make it possible for links to inherit styles from the surrounding text in case they are embedded? For example: Links might have a default 16px font, but that size should be overridden in case they are included in the middle of a paragraph made out of 14px sized text.

Timebox to 1 day

Event Timeline

Lydia_Pintscher renamed this task from investigation: links as components vs. reusable styles to investigation: links as components vs. reusable styles [timebox: 1d].Jan 27 2021, 10:09 AM
Lydia_Pintscher created this task.
Lydia_Pintscher updated the task description. (Show Details)

Some initial brainstormy thoughts:

Pro Component

  • better abstraction/encapsulation of "internals"
  • same argument as to why a button is a DS component and not just a set of styles
  • could maybe apply some validation on the URL to prevent mistakes like href="javascript:void(0)"
  • not providing two ways of doing things: components and classes

Pro CSS class

  • less confusing to translators if it appears in a i18n message
  • full control over all the possible behavior

Ideas for further investigation:

  • look at what vuetify and other component libraries are doing and try to infer their reasoning

Some initial brainstormy thoughts:

Pro Component

  • better abstraction/encapsulation of "internals"
  • same argument as to why a button is a DS component and not just a set of styles
  • could maybe apply some validation on the URL to prevent mistakes like href="javascript:void(0)"
  • not providing two ways of doing things: components and classes

Pro CSS class

  • less confusing to translators if it appears in a i18n message
  • full control over all the possible behavior

Ideas for further investigation:

  • look at what vuetify and other component libraries are doing and try to infer their reasoning

thanks for the points you made. I was originally pro css class. but I might be coming around to the component side.
I couldn't find much info on vuetify but I found another bootstrap library here

I think the encapsulation point you made is a solid one. especially, when it comes to behavior. e.g things like "href='#'" issue mentioned in the documentation.
also, I think the link is also supposed to support icons (optional), so I think cases like that make the case for components instead of css classes

So far, I focused mostly on implementation using mixing (since we have more experience with the component approach).

regarding the first criteria, mentioned above: It's possible to achieve the desired effect with mixins.

code snippet:

@mixin link($icon:url("/icons/link-icon.svg"), $position: 'left') {

   @if $position == 'left' {
       a:before {
         content: $icon;
         padding-right: 4px;
       }
    } @else {
         a:after {
            content: $icon;
            padding-right: 4px;
       }
    }
}

.text {
  @include link(url("/icons/custom-link-icon.svg"), 'right');
}



#HTML
<a class="text" href="#"> about Wikimedia </a>

as can be seen from the above sample code, we can pass two parameters to the mixin (the icon itself and the position of the icon)

P.S. more info can also be found here

regarding the second acceptance criteria:

Its a much rarer case. However, we can expand our mixin to accept a third parameter (font-size) to override the size. default value can just be set to 'inherit' so the link can inherit the font-size of the parent.

Implementation using props (vue components) can be accomplished easily.

an example can be found here

conclusion:

As far as implementation goes, there doesn't seem to be too much technical difficulty in going either way. echoing on some of the points @Michael mentioned above, I'll list some of the pros and cons.
I haven't found much info on veutify but on here and here , Links are used as components.

Pros: of component

  • Consistency: No need to create unnecessary learning curve, i.e. not introducing new way of doing things. we already use styled components so there no need to introduce a new way of doing things.
  • easy to publish a component to NPM and ensure that it is not only super customizable for the user through props and/or extending via styled(Component) but that it always looks & behaves as it did locally due to zero chance of clashing selectors.
  • easy to handle edge cases on the behavior of the link. e.g links with href="#". on click scroll to top of the page or what ?
  • we already have a story book

Pros of CSS styles:

  • easier to implement. i.e at least, code size wise

Cons of using CSS:

  • will be an additional way of implementing things, creating potential maintenance issues
  • has no story book. therefore difficult to document

@Lydia_Pintscher

I put all my findings here. please feel free if you've any questions

I looked it up and vuetify uses their button component v-btn for links, which is The Wrong Thing To Do. So that isn't providing any insight for us.

While I also slightly lean towards a component, I'm not sure that this is only our decision. I feel that the perspective of @ItamarWMDE and @Sarai-WMDE is needed as well.

The links to material-ui and bootstrap-vue point to another argument in favor of a component: We could, in the future, extend that to support routing libraries like vue-router.

@Michael yes! future extensions are another good reason for components.
I believe @ItamarWMDE and @Sarai-WMDE already had a deep dive into this topic. but maybe with the points mentioned above, that might help them get closer to a decision

@Michael yes! future extensions are another good reason for components.
I believe @ItamarWMDE and @Sarai-WMDE already had a deep dive into this topic. but maybe with the points mentioned above, that might help them get closer to a decision

Yes, the task description also mentions some "initial discussions". Could you point me to the notes of those initial discussions? That would enable us to take their insights into account and would allow us to build on top of it instead of duplicating effort.

@Michael unfortunately , I don't have the notes, as I was note part of the meeting.
but maybe @Sarai-WMDE has them ?

Itamar and I had only an initial discussion on Mattermost, where the working idea of creating the WiKit link as a set of classes rather than as a component was introduced by him. I later brought up that approach to the hike while looking at the Link component ticket together, and detected the need for an investigation ticket. I believe that the outcome of this investigation is an excellent starting point! Thanks so much, Bereket and Michael.

Regarding the component approach, and related to the second criteria: how can we make sure the component is embeddable? Meaning, that it can inherit the styles of surrounding text when used inline. (I believe this could probably be a variant or a prop, but just wanted to make sure we keep this in mind)

Regarding the component approach, and related to the second criteria: how can we make sure the component is embeddable? Meaning, that it can inherit the styles of surrounding text when used inline. (I believe this could probably be a variant or a prop, but just wanted to make sure we keep this in mind)

That depends on the specific declarations. For example a font-size of 1em will exactly use the font-size of the surrounding <p>. Also, line-height could either be plainly not defined or be set to line-height: inherit;. Similarly, for font-family, etc.

On the other hand, there might be cases where we don't want inheritance, for example color: #3366cc; will use exactly that color,and in that case this is what we almost always want.

We might be able to gain some insights by considering the "embedded" link the default and try to think about situations where it is not embedded. Maybe something like a "Next" link when flipping through some pagination?

The color of links should never be inherited, exactly; only the font-family, size, weight and line-height of embedded links should be adjusted, if needed, to match the surrounding text.

Is it safe to assume that we'd probably need two link variants then? A Link (with its own, autonomous style – e.g. our "Show query in the Query Service") and an Inline link (which style blends with that of the text where it's embedded, but that it's still recognizable as a link, e.g. the "SPARQL" and "Feedback is welcomed here" links in the QB introductory description).

Edit: It's also worth mentioning that inline or embedded links don't need icons.

For reference, there's an example of this 2 variants' approach in IBM's Carbon Design System.

Is it safe to assume that we'd probably need two link variants then? A Link (with its own, autonomous style – e.g. our "Show query in the Query Service") and an Inline link (which style blends with that of the text where it's embedded, but that it's still recognizable as a link, e.g. the "SPARQL" and "Feedback is welcomed here" links in the QB introductory description).

It is entirely possible to instead have a prop that says something like inheritStyles=true or isStandalone=false.

Rather, I wonder if a standalone component would make more sense in cases where the standalone-link is supposed to be more prominent. Like the "Get Started >"-Link on https://design-system.service.gov.uk/. It is clearly a link - conceptually, visually, behaviorally, technically:

Is it safe to assume that we'd probably need two link variants then? A Link (with its own, autonomous style – e.g. our "Show query in the Query Service") and an Inline link (which style blends with that of the text where it's embedded, but that it's still recognizable as a link, e.g. the "SPARQL" and "Feedback is welcomed here" links in the QB introductory description).

It is entirely possible to instead have a prop that says something like inheritStyles=true or isStandalone=false.

Rather, I wonder if a standalone component would make more sense in cases where the standalone-link is supposed to be more prominent. Like the "Get Started >"-Link on https://design-system.service.gov.uk/. It is clearly a link - conceptually, visually, behaviorally, technically:

I'd go with the props option. having two separate types of links might create confusion

We could, in the future, extend that to support routing libraries like vue-router.

This argument is enough to win me over. Usually I'm not in favor of creating components simply for styling purposes, and since we already expose a global CSS file that is used by consuming applications, I don't necessarily see it as a problem to scope styles as a reusable CSS class that is available to implementing applications. However, once you add state and behavioral considerations into the mix, it makes a little more sense to me.

Either or, I will back your decision even if there were no such considerations, as the research seems well thought of and there are other compelling arguments.

I would ask though, how do you envision the API of this component to work? Is it something like <Link type="external">...</Link>?

We could, in the future, extend that to support routing libraries like vue-router.

This argument is enough to win me over. Usually I'm not in favor of creating components simply for styling purposes, and since we already expose a global CSS file that is used by consuming applications, I don't necessarily see it as a problem to scope styles as a reusable CSS class that is available to implementing applications. However, once you add state and behavioral considerations into the mix, it makes a little more sense to me.

Either or, I will back your decision even if there were no such considerations, as the research seems well thought of and there are other compelling arguments.

I would ask though, how do you envision the API of this component to work? Is it something like <Link type="external">...</Link>?

yes, that was my idea for the API

It is entirely possible to instead have a prop that says something like inheritStyles=true or isStandalone=false.

The prop option sounds good to me too.

Can we consider the investigation done, then? I'd update the component ticket accordingly. Thanks again for the enlightenment 🙏🏻

It is entirely possible to instead have a prop that says something like inheritStyles=true or isStandalone=false.

The prop option sounds good to me too.

Can we consider the investigation done, then? I'd update the component ticket accordingly. Thanks again for the enlightenment 🙏🏻

yesss!!!
very good insights from @Michael and @ItamarWMDE

amy_rc added a subscriber: amy_rc.

💮 closing the ticket as the investigation is done.