Page MenuHomePhabricator

[Spike 4hrs] Identify the component styling baseline for Vue.js search
Closed, ResolvedPublicSpike

Description

Similar to the component baseline choice (T253933), a complementing styling technical baseline should be chosen. Styling is needed for the components themselves, layout, definitions, and more. What baseline should we start with for Vue.js Search ?

The motivations are pretty similar to T253933.

Options

Among the options are:

Approaches

  • File copy
  • In-between: E.g., pieces of mediawiki.ui with new class names and Wikimedia UI.
  • Blank slate

Concerns

  • Too much legacy code to innovate vs too little to build upon
    • At least for #Vue.js-Search, efforts should be categorized as weeks, months, or years of effort.
  • Naming conflicts with existing styles.
  • Migration paths.
  • Bandwidth consumption. #Vue.js-Search adds a significant new dependency to the main namespace, Vue.js itself. We must try especially hard to to minimize bytes shipped during this time.
  • Risk loosing internationalization and accessibility measurements on the way if not done appropriately, see T249300
  • Deviations from Wikimedia Design Style Guide component blueprints, resulting in a non-aligned component.

Related concerns to consider but not necessarily resolve right now

  • Surfacing theme to the top level and allowing components to be only structure.
  • Enabling vs forbidding styling usage in content. E.g., if scoped styles are used this makes usage in content blocked by obfuscation.
  • Grid seems useful but big. Is this something we can use for #Vue.js-Search?
    • What changes are needed to the current version to use in #Vue.js-Search only? A little? A rewrite? Somewhere in between?
  • Code structure and splitting strategies.

Conclusions

Acceptance criteria

  • A baseline option is chosen. > See above, combination of WikimediaUI Base variables as baseline design decisions, CSS properties from OOUI as best-practice application and BEM(-derived) naming convention
  • A task or tasks are made for creating or copying any of the existing styles over to the library. > Will be executed in new component specific tasks.

Event Timeline

Restricted Application changed the subtype of this task from "Task" to "Spike". · View Herald TranscriptMay 29 2020, 5:20 AM
Restricted Application added a subscriber: Aklapper. · View Herald Transcript

OOUI (which uses WMUI base) represents our best efforts in terms of accessible markup and styling. MediawikiUI is legacy and mostly unmaintained.

Any component which doesn't at least start from the OOUI DOM attributes/styling will likely introduce accessibility regressions.

Thank you, @Esanders. That's my understanding as well. However, I don't have a grasp for the level of effort required to migrate this code to something practical for Vue.js search. Do you think these styles can be split out and used for new components (with possibly different structures)? For example, if I had a button and an input from T253933, would applying OOUI styling take days, weeks, months, or longer? If it is going to take longer than the time allocated to #Vue.js-Search, what do you think we should do for the search components specifically?

I don't think it should take long, you just need to look up the relevant styles in the OOUI LESS files. A few hours for the button widget, maybe a day.

To share from Wikimedia Design and my Design Systems SME position:
We need to aim to build a structurally semantic, accessible and flexible (open for combining different components relatively free and also consider being themeable) collection.

From all the ones mentioned in the task description, WikimediaUI Base is the most accurate source of truth, it's currently in use by WikimediaUI theme in OOUI and a number of other products.
We've evolved WikimediaUI theme in OOUI, there are clearly good and better parts, which we were to be smart to integrate. Still, I'd take the Vue.js components development as opportunity to clean-up some of the 6+ years old architectural thoughts behind OOUI. A few examples:

  • Icon/Indicator implementation. Indicators (only 4 left) should be just a different icon and be handled at best purely by CSS. They shouldn't been even featured as great implementation example any more. Overly complex architecture, and breaking separation of logic and presentation
  • OOUI widget DOM structure. A huge flexibility for mixing in numbers of other widgets into a containing widget. That flexibility has IMHO not seen popularity or wide appliance, but made architecting new widgets much harder.
  • OOUI components default DOM semantics or actual non-semantics. In big parts owed to cross-browser support issues at the time of library start, things like ButtonWidget being an a element instead of a button element and then re-building all the inherent accessibility tree semantics (role=button) on them should be avoided.
    • @Sniedzielski brought up another excellent example of unused potential of standards HTML – the disabled attribute and its :disabled pseudo class. Originally not done due to non-support by IE6-8, having to manually implement it with a class/aria-disabled approach leading to more code.

One unknown here is server-side rendering of the widgets, but I guess if we come up with the perfect component DOM, CSS class architecture and styling, we can re-build the more simple components as good as possible, similar to what is achieved in OOUI PHP.

I agree with @Esanders that most of the mediawiki.ui classes and properties don't make sense any more, there are very few, very limited components written with it and relatively unmaintained, the good parts of mediawiki.ui can be found in OOUI's WikimediaUI theme. For a moment, I considered having some basic classes similar to mediawiki.ui, but it is questionable that a simple non-leveled class system would scale within higher complexity widgets. See related exploration of technology-agnostic vs non-agnostic Design System by WMDE with similar result to our OOUI path back then – relying on a non-agnostic approach.

I think the OOUI styles are worth leveraging, with some of the caveats that @Volker_E mentioned. In addition to upgrading the DOM structure, I think we should seriously consider shortening some of the class names used in OOUI, which can account for a large portion of the actual CSS payload (see this article: Reducing CSS bundle size 70% by cutting the class names and using scope isolation for details). Vue's scope CSS can help us do that.

One way we could try to integrate OOUI styles into a newer DOM structure is taking advantage of the fact that all classes in Less can be used as mixins as well. That could allow us to do something like the code below. I remember I tried doing this a while back to see if it'd work (your mileage may vary), codesandbox here and results here

.vue-btn {
    .oo-ui-buttonElement-button;
    .oo-ui-buttonElement-frameless;
    .oo-ui-widget-disabled;
}

@Jdrewniak Could you expand on the bundle size problem and the possible advantage that you see?
We've been looking into reducing class names in OOUI, with ridiculously little gain after gzipping, not worth the effort back then.

@Volker_E huh, I forgot about that, thanks for reminding me. I actually had two concerns with the class-name sizes: 1.) developer ergonomics/convenience, 2.) performance.

On the convenience side, OOUI is built with an inheritance model that chains most of the complicated class-names for you, so you never actually have to write a class-name like <span class="oo-ui-buttonElement-frameless oo-ui-indicatorElement oo-ui-iconElement oo-ui-labelElement"> by hand, even though that is the final output. Moving to Vue, we probably would be writing those classes by hand, which IMO, sounds like a bit of a hassle.

The other aspect, performance, is certainly interesting. With Vue, we have the potential to highly optimize the CSS output with the help of a build-step, using tools like scoped styles and even shortening the class-names with something like CSS-modules, which has this functionality built-in. I was curious to see if these highly optimized class-names are actually worth it (as that article I posted above implies) so I made a demo to test out what would happen if I replace all the class selectors in resources/lib/ooui/oojs-ui-core-wikimediaui.css with shorter 6 character hashed versions instead. Similar to your findings, the uncompressed versions saved a bit of space, 53 vs 72 kB, but the gzipped versions had a minimal difference, at 7.7 vs 8.1 kB. demo here.

So I guess performance isn't really a valid concern.

ovasileva renamed this task from [Spike] Identify the component styling baseline for Vue.js search to [Spike 4hrs] Identify the component styling baseline for Vue.js search.Jun 23 2020, 5:16 PM

On the convenience side, OOUI is built with an inheritance model that chains most of the complicated class-names for you, so you never actually have to write a class-name like <span class="oo-ui-buttonElement-frameless oo-ui-indicatorElement oo-ui-iconElement oo-ui-labelElement"> by hand, even though that is the final output. Moving to Vue, we probably would be writing those classes by hand, which IMO, sounds like a bit of a hassle.

I'd recommend that we rely on computed properties to do most of the work when it comes to applying lists of classes to various elements. Vue is very smart when it comes to applying class and style bindings in component templates: you can bind class and style attributes directly to an object (whose keys represent different class names or CSS properties, respectively) and avoid having manually concatenate or manipulate string attribute values. When combined with a computed property, you can dynamically apply a large number of classes or styles in a concise way. It's also okay to have class and v-bind:class attributes on the same element, so you can distinguish between classes which are always present (wvui-button) and classes which are conditional (wvui-button--disabled, etc). This combines well with a BEM approach; most of your modifier classes could come from a computed property. @Niedzielski included a good example of how this could look in the Vue/OOUI migration document; we're also using the same approach in the MachineVision Vue components (including the distinction between static and dynamic class attributes).

{
    name: 'wvui-button',
    props: {
        progress: {
            type: String,
            default: 'Default',
            validator( val ) {
                return [ 'Default', 'Progressive', 'Destructive' ].includes( val );
            }
        },
        framed: Boolean
    },
    computed: {
        classes() {
            return {
                'wvui-button': true,
                'wvui-button--progressive': this.progress === 'Progressive',
                'wvui-button--framed': this.framed,
                'wvui-button--quiet': !this.framed
            };
        }
    }
}

Regarding the long list of class names you often see in OOUI, I can't speak to the performance question but having a ton of classes on each element does make the code harder to read in the browser inspector tab (if you're trying to dig through markup to figure out what exact set of classes is getting applied to an element and why – browsers often truncate this if there is too much text). In Vue this may be less of an issue because we'll be able to rely on the Vue.js Devtools for a more transparent look inside components at runtime.

Volker_E updated the task description. (Show Details)