department-of-veterans-affairs / vets-design-system-documentation

Repository for design.va.gov website
https://design.va.gov
36 stars 55 forks source link

Discovery: How to handle styling slot content for USWDS v3 #2298

Closed ataker closed 4 months ago

ataker commented 5 months ago

Description

When creating v3/USWDS components, we import USWDS styles and apply them by marking up the component with USWDS classes (starting with usa-). However, when we accept slot content it is isolated from outside styles, including USWDS, and so the content remains unstyled.

Details

We need a plan for how we want to handle applying these styles. Do we want to repeat these styles in our own stylesheets? Do we want to accept less slot content and make our components more tightly structured? Do we need to dynamically apply classes and load some USWDS modules globally?

Related PR

Tasks

Acceptance Criteria

caw310 commented 5 months ago

Hey team! Please add your planning poker estimate with Zenhub @Andrew565 @ataker @harshil1793 @it-harrison @jamigibbs @micahchiang @nickjg231 @powellkerry @rmessina1010 @rsmithadhoc

jamigibbs commented 5 months ago

Here is an example of what we're trying to solve for. This is the USWDS summary box which we are wanting to use for va-featured-content. This web component uses two slots; one for the heading and one for the content.

Classes are dynamically being applied to the slots in va-featured-content but the styles are not applying from the USWDS summary box module.

<div
  class="usa-summary-box"
  role="region"
  aria-labelledby="summary-box-key-information" <-- does not connect into heading slot
>
  <div class="usa-summary-box__body">
    <h3 class="usa-summary-box__heading" id="summary-box-key-information"> <--- SLOT HEADING. USWDS style classes do not work.
      Key information
    </h3>
    <div class="usa-summary-box__text"> <--- SLOT CONTENT. USWDS style classes do not work.
      <ul class="usa-list">
        <li>
          If you are under a winter storm warning,
          <a class="usa-summary-box__link" href="">find shelter</a> right away.
        </li>
        <li>
          Sign up for
          <a class="usa-summary-box__link" href=""
            >your community’s warning system</a
          >.
        </li>
      </ul>
    </div>
  </div>
</div>
rmessina1010 commented 4 months ago

In going about this discovery I made a few of base assumptions for sanity's sake. 1) The web-component itself insures that the desired class is added to the slot elements ( as in the case in va-featured-content) 2) That the questions asked in the description were done so as a means to an end, but the primary goal was to apply the USWDS styles to the team provided content and that the current preferred method method of providing said content was slots ( which could entail all manner of composition); this is to say that this discovery's original search favors any solution which will not require restructuring our elements 3) We are OK with the styles being applied in the light DOM (globally) or the Shadow DOM; Tho I presume if we can pass the styles globally it would be simpler and more performant.

My initial premise: Look for way to leverage the ::slotted() pseudo element and have the USWDS CSS rules passed as additional selectors. However, ::slotted() works on the shadow DOM. Which, would only had meant having to import and tweak of the the needed styles in to the component, except ::slotted() only takes a simple or compound selector, that means only the very top element would receive the styling. Documentation proved that this was a conscious (W3C?)decision ::slotted() to target descendants. Sub conclusion: ::slotted(). can only be used IF our style declaration are made/imported we are 100% sure the content passed in the slot is a singular element; which seems like an unrealistic constraint.

Conclusion: ::slotted() will not give the outcome as needed, looking into other solutions.

Articles of interest: https://stackoverflow.com/questions/61626493/slotted-css-selector-for-nested-children-in-shadowdom-slot/61631668#61631668

https://stackoverflow.com/questions/64477437/how-to-css-styling-inside-the-slot-in-custom-element

rmessina1010 commented 4 months ago

Alternatives:

1) import class directly into component style (shadow-root only!), does not seem to work, in my findings thus far. Again I think it is because slotted content brings in its styling from the light DOM; styles defined in the web component apply to the shadow DOM.

2) placement of class in slot component. What this entails is adding a new wrapper element to the assignedElements nodeList, moving the original elements inside the wrapper. the wrapper, not the slot, gets the class applied to it. ++ Styles are applied from the light DOM, in theory, there is no need to import them separately into each component -- sort of hacky and the additional wrapper element may require inspection and reconfiguration of tests (unit/E2e, etc) .. Please note, this could be achieved manually instead, if developers wrapped a div around their slotted content and applied the classes at that point https://github.com/department-of-veterans-affairs/component-library/pull/1015

3) ::part() selector... pending [my confusion thus far is I don't yet see how the part attribute could be applied to a slot element, w/o some hack]

jamigibbs commented 4 months ago

@rmessina1010 Thanks for digging into this!

Do you have a working Chromatic link for this draft PR by chance for us to preview?

jamigibbs commented 4 months ago

@rmessina1010 Would programmatically adding the classes to the slot work (like in your draft PR) for elements like p and a that are added to the slot? I ask because USWDS has styling classes for content like that as well. Here is a list of all of the css classes I'm seeing in their component code example.

ataker commented 4 months ago

Currently getting an error: typeError: el.appendChild is not a function at createNewBlankDiv

.. Please note, this could be achieved manually instead, if developers wrapped a div around their slotted content and applied the classes at that point Will putting in linters to push people to set up their slot content correctly will work? It seems like too much of an ask unfortunately...

rmessina1010 commented 4 months ago

Addendum I:

Researched into ::part() pseudo element. This proved not to be the solution we are looking for, as the part attribute cannot be assigned to the slot element. The intended use of parts is to style specific elements in the elements template as if they were in the light DOM.

Addendum II:

We could move the contents of slots, into the shadow DOM of the custom-HTML-elements, taking care not to the structural order. This would allow all the shadow DOM custom HTML element styles to apply, at the cost of having those slots (behavior wise). Consequently, the desired styles need to be imported into the custom HTML element. Also note that as such selectors with ancestor to the desired classes will not be honored.

At this point we would also need to consider, if we want to do this operation for slots we are adding these specific classes to OR all slots regardless ( so as to have a consistent behavior throughout all custom elements)

PR in progress here: https://github.com/department-of-veterans-affairs/component-library/pull/1022

rmessina1010 commented 4 months ago

@jamigibbs, ( thinking you mean slots in the template of custom element) my experience is the class gets added to the slot (no need for a function), but slot is ignored. So having slot.usa-anyclass had no effect on styling on slot decedents.

jamigibbs commented 4 months ago

@rmessina1010 Maybe this was done already but have you looked into just loading specific USWDS modules globally?

So like if we programmatically add a class to a slot element like usa-summary-box__link to all anchor tags in the va-featured-content component, the styles would not be encapsulated anywhere.

In other words, can @use 'usa-summary-box/src/styles/usa-summary-box'; be imported globally? I realize that mean this style will be loaded regardless of it the component is on the page but I don't think this is something we'd need to do very often.

Something similar is done here:

https://github.com/department-of-veterans-affairs/component-library/blob/main/packages/web-components/src/global/main.css

@import '~@department-of-veterans-affairs/css-library/dist/tokens/css/variables.css';
@import '../components/va-breadcrumbs/va-breadcrumbs-slot.css';
@import '../components/va-modal/va-modal-slot.css';
@import '../components/va-table/va-table-slot.css';
@import '../components/va-alert/va-alert-slot.css';

va-process-list li :is(h2, h3, h4, h5) {
  margin-top: 0;
  clear: none;
  padding-top: 0.3rem;
}
rmessina1010 commented 4 months ago

@Caw310 I think I missuderstood this ticket. I was trying to get CSS to work genrally, when in fact thew solution we might be looking for is to have the USWDS styles in the global stylesheet, and attach classes to elements being passed as slotted content; essentially parsing the content an attaching classes. Am working on this 3rd PR but may need to carry over.

rmessina1010 commented 4 months ago

PR 1030 Discovery doc here

rmessina1010 commented 4 months ago

For continuity and reference: This PR, that should be part of a separate issue, attempts to address import and separation of USWDS styles. https://github.com/department-of-veterans-affairs/component-library/pull/1038

rmessina1010 commented 4 months ago

Conclusion

After going over the problem with the team, it was decided the best solution to pursue is to:

  • Assign styles in the global space (light DOM) of vets-website.
  • Employ the utilities defined in PR 1030 to automatically add required USWDS attributes to slotted components.
  • Import/copy only the USWDS rules pertaining to the specific elements that would be slotted in corresponding web components.

Rationale

Pursuing this approach retains the behavior of slotted elements that VFS engineers expect, including the option for them to add their own utility classes and other attributes as needed; as such it also streamlines maintenance of va-components. In addition, I expect that as we migrate to USWDS, many of the styles utilized in va-components will also be needed generally, so adding them to the global scope would have eventually happened as well.

Downsides

It is possible may need to take care to avoid/handle encounter specificity collisions.

Why we did not choose to use the Shadow DOM

While moving the slotted nodes into the shadow DOM would have helped with encapsulation of styles. The same would have resulted their expected styling and access behavior, required a large amount of coordination, and placed new limitations and additional maintenance on VFS teams.