Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The Future of FAST Foundation #5901

Closed
EisenbergEffect opened this issue Apr 27, 2022 · 39 comments
Closed

The Future of FAST Foundation #5901

EisenbergEffect opened this issue Apr 27, 2022 · 39 comments
Assignees
Labels
area:fast-foundation Pertains to fast-foundation docs:rfc Request for Comments (proposal) epic A collection of features and tasks with a common objective. status:in-progress Work is in progress

Comments

@EisenbergEffect
Copy link
Contributor

EisenbergEffect commented Apr 27, 2022

Last week @chrisdholt shared our vision for the future of FAST Components. In that post, we briefly touched on how FAST Foundation might evolve and play a more prominent role in the future, combined with the introduction of our new command-line interface (CLI). I want to take some time now to expand those ideas, paint a bit of a technical vision for the future, and show you how that might unfold over a series of iterations.

The Big Picture: Design System as Code

Infrastructure as Code (IaC) is the management of infrastructure (networks, virtual machines, load balancers, and connection topology) in a descriptive model, using the same versioning a DevOps team uses for source code. Like the principle that the same source code generates the same binary, an IaC model generates the same environment every time it is applied. IaC is a key DevOps practice and is used in conjunction with continuous delivery.

From "What Is Infrastructure as Code?"

Wait. What? Isn't this post supposed to be about FAST Foundation and Design Systems?

Why yes, it is.

The goal for FAST Foundation and what we call "Adaptive UI" has always been to get us to what I'm going to refer to as "Design System as Code". Here's a simple definition inspired by the above.

Design System as Code (DSaC) is the management of a design system (components, templates, styles, tokens, layers, and visual algorithms) in a descriptive model, using the same versioning an engineering team uses for source code. Like the principle that the same source code generates the same binary, a DSaC model generates the same design system every time it is applied.

We believe that DSaC will become a essential DesignOps and UX Engineering practice, used in conjunction with experimentation and continuous delivery.

To provide some light justification for this claim, let's look back at a trend in the broader industry over the last couple of decades. We can see a common pattern that goes something like this (using DevOps and IaC as an example again):

  1. Problem identified
    • "Deploying and operating scalable web infrastructure is hard!"
  2. Bespoke solutions implemented
    • "We should create a bunch of scripts to help us deploy and operate stuff!"
  3. 1st generation libraries/frameworks/tools emerge
    • Puppet, Vagrant, etc.
  4. Standardization begins
    • Docker, Cloud Native Computing Foundation, etc.
  5. 2nd gen libraries/frameworks/tools and "IaC" emerge
    • Kubernetes, Terraform, Ansible
  6. Full operational systems and "X as a Service" available
    • e.g. Azure DevOps

(I should note that step 6 often comes before 4, depending on market opportunity. Eventually, it repeats itself later after standardization occurs, where the original provider deprecates the previous version and ships a new offering or they make significant changes to the existing product to support standards. The reality of the innovation/standardization tug-of-war is always more complex, but hopefully, this still makes its point.)

Ok, before we get too far off track, let me pull this back to design systems. This post isn't intended to analyze the history of software industry trends or DevOps. It's about where we're going with FAST Foundation.

So, let's take the IaC and DevOps sequence from above and look at it in terms of design systems.

  1. Problem identified
    • "Building reusable components and design systems is hard!"
  2. Bespoke solutions implemented
    • "Let's write a bunch of CSS we can share!"
  3. 1st gen libraries/frameworks/tools emerge
    • Bootstrap, Foundation, Semantic
    • Angular, React, Vue
  4. Standardization begins
    • Web Components, OpenUI, W3C Design Tokens
  5. 2nd gen libraries/frameworks/tools and "DSaC" emerge
    • FAST Foundation
  6. Full operational systems and "X as a Service" available
    • ???

Interestingly, the design system industry was stuck in phase 3 for a very, very long time. It is only within the last two years that phases 4 and 5 have begun. This stall was mainly due to Web Components taking about 7 - 8 years of standards work to ship in every browser. That was a huge milestone that unlocked the industry and enabled us to move forward with FAST.

The Past, Present, and Future of FAST Foundation

As it exists today, FAST Foundation was a first attempt at a 2nd generation framework for design systems. We think it's pretty nifty, but we're also keenly aware of its shortcomings and limitations (thank you, the community, for all the amazing engagement and feedback over the last two years!). In the following few sections, we'll look at the previous and current states of FAST Foundation as an initial phase 5 offering, and then I'll walk you step-by-step through the changes we're considering and show you how we're going to unlock the path to phase 6.

Iteration 1 (Past)

The @microsoft/fast-foundation library started as a collection of component base classes and templates. We used it to build the first version of the Fluent UI Web Components. As the community came on board, we got a lot of feedback, mainly that things weren't configurable. For example, component names, prefixes, shadow DOM modes, etc. couldn't be changed. So, we built a new set of APIs that led us to...

Iteration 2 (Present)

@microsoft/fast-foundation now consists of component base classes, template factory functions, and various primitives, such as a design token object model and a design system configuration model. It's a good start at building the runtime of a DSaC system, and we used it to build v2 of the Fluent UI Web Components, which are the foundation for various experiences in Edge, Windows 11, and MSN. However, FAST Foundation still has a few problems:

  • It's hard to get started building a new design system.
  • It's impossible to customize the templates in certain ways.
  • There's a lack of "governance" features for systems built on foundation components.
    • e.g. locking down CSS parts for design system consumers isn't possible
  • There's no solution for styles that works like templates. You have to write and manage all of that yourself.
  • It's hard to learn how to properly use Adaptive UI and design tokens.
  • All the customization happens at runtime, which has a cost.
  • Configuring the system is ugly and doesn't follow standard Web Component patterns.

Most people don't experience these problems through FAST Foundation but through what we've previously called "FAST Components," which introduced another set of challenges:

  • Overriding the templates is ugly.
  • Overriding/extending styles is ugly.

Ultimately, this method just isn't working. It's not meeting the community's needs, nor is it giving us the power we need to move forward with v3 of the Fluent UI Web Components. The next set of iterations lays out how we will solve these problems.

Disclaimer: Everything from here on out is speculative. Code is just for example purposes, and we may shift the order and composition of the various features in the end.

Iteration 3 (In Progress)

The next iteration isn't so much a change in @microsoft/fast-foundation as it introduces the FAST CLI. The CLI is intended to address several of the biggest problems we've seen the community struggle with:

  • It's hard to get started building a new design system.
  • It's hard to learn how to properly use Adaptive UI and design tokens.
  • Overriding/extending styles is ugly.

The CLI will provide a command to generate a design system similar to the following.

$ fast add-design-system --prefix fluent

What we previously shipped as a component library in @microsoft/fast-components will now be a set of templates for your design system. The goal is that within a couple of minutes, you should be able to generate a new design system and then immediately be able to run Storybook to see all your Web Components in action. You should also be able to add new Foundation components as needed with a simple command like this:

$ fast add-component --foundation button

Based on the feedback we've heard from the community, we think this will significantly improve your experience building design systems right out of the gate. Once you no longer fight against a preset package of components like @microsoft/fast-components but instead own the design system code directly, working with them becomes much more manageable. You'll be able to look at the styles for each component and see how the tokens are used. If you need to add custom styles, it's no longer a series of arcane overrides. If you need to introduce your design tokens on top of the base tokens, you just create them and use them in the styles you control, following existing patterns you can see. Many things get much easier to use and learn.

Iteration 4 (Future)

While Iteration 3 puts a lot more control into your hands and gets you up and running faster, the underlying programming model is still burdened by old APIs that were designed based on needing tons of runtime configuration and overrides. Iteration 4 will remove that code, addressing the following problems:

  • All the customization happens at runtime, which has a cost.
  • Configuring the system is ugly and doesn't follow standard Web Component patterns.
  • There's a lack of "governance" features for systems built on foundation components.

We'd like to remove the registration APIs that enabled overrides. These are no longer needed now that you control the design system code. We can then move the Web Components back to a normal registration approach. The CLI templates will update in lockstep with this and will still support centralizing various configuration details using standard JavaScript techniques. To illustrate this, let's look at a few code samples.

First, the CLI will create a simple constant to store shared configuration details. This constant will be generated based on the answers provided to CLI prompts when you create a new project.

design-system.ts

export const designSystem = Object.freeze({
  prefix: "fluent",
  shadowRootMode: "open",
  registry: customElements
});

The component code will follow standard patterns that give you full control. Let's look at how a button might be defined in your design system project. Here's what the folder layout could look like:

button/
└─ fixtures/
   └─ button.html
└─ button.ts
└─ button.template.ts
└─ button.styles.ts
└─ button.definition.ts
└─ button.stories.ts
└─ define.ts

First, the core component class (state, behavior, etc.) doesn't change from today's implementation. The CLI generates a class for you based on the appropriate foundation component base class.

button.ts

import { FASTButton } from "@microsoft/fast-foundation";

export Button extends FASTButton {
  // You can add your behavior to your button here if needed.
}

Second, the template for your component also doesn't change (see Iteration 5 below for how we'll introduce customization in the future). The CLI generates a .template.ts file with a simple export of the foundation template.

button.template.ts

import { buttonTemplate } from "@microsoft/fast-foundation";

export const template = buttonTemplate();

The styles are handled in much the same way. The CLI will generate default styles for you to start.

button.styles.ts

import { css } from "@microsoft/fast-element";

export const styles = css`
  The CLI will provide you with base styles for each component.
  You can modify them to your heart's content.
`;

The first new part is that we generate a .definition.ts file. This file contains all the metadata that FASTElement needs to define the component.

button.definition.ts

import { designSystem } from "../design-system.js";
import { Button } from "./button.js";
import { template } from "./button.template.js";
import { styles } from "./button.styles.js";

export const definition = Button.compose({
  name: `${designSystem.prefix}-button`,
  template,
  styles,
  shadowOptions: {
    mode: designSystem.shadowRootMode,
    delegatesFocus: true 
  }
});

The second new part is that we generate a define file that invokes FAST's define API, provided by the definition, against your default registry.

define.ts

import { designSystem } from "../design-system.js"
import { definition } from "./button.definition.js";

definition.define(designSystem.registry);

The above code, which would all be generated by the CLI, provides complete customization of the component base class and styles and centralized configuration for the design system. With this new setup, we can enable these capabilities without the runtime cost of the current FAST Foundation provideDesignSystem().register() APIs. You also retain complete control over when/how the platform defines the component. If you want to auto-define components, import the define module wherever you use the component.

import "@fluentui/web-components/dist/esm/button/define.js";

The CLI can even configure package.json entry points for you so that you can have shorter paths.

import "@fluentui/web-components/button";

If you want to control the platform more, import the definition and call define yourself whenever you want. You'll even be able to leverage the upcoming W3C Scoped Element Registries feature as soon as it's available.

Notice how this approach now also introduces the first governance features. By removing the runtime configuration APIs, you can now prevent consumers of your design system from overriding styles, changing tokens, or messing with your templates. You still have full control, but you can now guard against misuse.

Iteration 5 (Future)

Things are starting to come together, but we've still got problems. Iteration 5 seeks to address the following issues:

  • It's impossible to customize the templates in certain ways.
  • Overriding templates is ugly.
  • There's a lack of "governance" features for systems built on foundation components.

Up to this point, @microsoft/fast-foundation only provides you with pre-constructed templates for each component. That's great for most cases, but a few scenarios aren't possible. Examples include:

  • You can't add/remove/change slots.
  • You can't add/remove/change parts.
  • You can't add new HTML before, after, or around the existing markup.

To address these needs, I'm proposing that in addition to exporting pre-built templates for components, we also export "template builders," which can be used to construct OpenUI-based template variations.

Note: Not familiar with Open UI? Open UI is a W3C Community Group that maintains an open standard for UI, backed by research into components and controls across the web and native platforms. The group aims to promote adherence, adoption, and interoperability across the industry.

Let's look at an example of using a ButtonAnatomy template builder to customize an Open UI-based button template.

button.template.ts

import { designSystem } from "../design-system.js";

export const template = build(ButtonAnatomy)
  .cssParts(designSystem.exposeParts) // boolean or filter
  .anatomy(x => { // button-specific anatomy
    x.startSlot({ name: "prefix" }) // change the name from "start" to "prefix"
     .defaultSlot()
     .insert(...); // augment with custom HTML after the default slot
     // .endSlot() // no end slot please
  }).build(); // returns a ViewTemplate for use with your component

These aren't the actual APIs, but hopefully, this serves as an example of what we could enable. We want to make @microsoft/fast-foundation into more and more of a toolkit that helps you build design systems following Open UI patterns. This is the first iteration where something that resembles DSaC starts to emerge, with a fully-typed object model for Open UI-based Web Components.

Important: With each iteration from here out, we're adding capabilities and not removing them. So, if you want to use the pre-built templates and not bother with the builder API, those will remain present. That will be an excellent way for newcomers to get started. However, as you get familiar with the system and find a need for the builders, you can opt-in. The CLI can help generate default anatomy for you to get started. You'll be able to do this on a component-by-component basis.

Once again, you may be thinking, "Wait. What? Wasn't it a goal to have customization without runtime cost? This seems like it could impact startup time."

On that note...

Let's talk about a parallel effort already in development as part of the FASTElement 2.0 stream of work: FAST Template Pre-compilation.

So, what's the runtime cost of the template builder code and template customizations?

ZERO.

Actually, the costs are less than zero. We have tech we're working on that allows us to run the template builder code...at build time. The result is an entirely pre-compiled template, ready for immediate instantiation. The builder code, FAST's html template function, and the full FASTElement template compiler can all be tree shaken away. You are left with precisely what you need to render your component. Nothing more.

Note: A nice side effect of this is that we'll be able to validate the templates at build time. This will enable us to provide more meaningful compile-time errors and prevent runtime issues.

We'll be talking about this more in the future. I want to let people know that this is being built in a general-purpose way for use with all your FAST-based Web Components and with whatever bundler you want.

Iteration 6 (Future)

There's still one little problem left to address:

  • There's no solution for styles that works like templates, and you have to write and manage all of that yourself.

Hopefully, you can start to see where we're going with this. The CLI has been generating starter CSS into projects for each component. In Iteration 6, we'll introduce a CSS API for Open UI-based Web Components, similar to the templating API. Additionally, we'll enable the CLI to generate a default CSS configuration using this API. This iteration is the most speculative part of what we're proposing, but you can imagine something like this:

button.styles.ts

export const styles = build(ButtonPresentation)
  .tokens(...) // map design tokens and/or CSS variables to semantics
  .standardStates() // support standard states like disabled
  .features(x => { // style features specific to buttons
    x.appearance(accent)
     .appearance(outline)
     .appearance(stealth)
     .appearance(customAppearance)
     .icons(); // add styling support for slotted icons
  })
  .forcedColors() // High Contrast and a11y FTW!
  .build(); // returns ElementStyles for use with your component

Naturally, we'll use our pre-compilation technology to run this at build time so that you have optimized CSS that needs no further processing to render. This process allows us to tree shake the builder and shake out the css template helper, CSS directives, and associated runtime code.

Beyond FAST Foundation

We have arrived at something we can call "Design System as Code," enabled by design tokens, standard component behaviors, and standard anatomy/presentation object models. However, there's much more we can unlock once we get to this point. It's just the beginning. For example, the builder code shown above has a declarative look. Could a JSON or Yaml language be mapped over the builders? Could tools be created to emit this JSON? There are lots of exciting opportunities.

Wrapping Up

We're not satisfied with the state of design system creation in the industry. We believe we've made solid strides in improving it with what we've got in FAST today. However, we're not there yet. Above I've outlined what I think are the next set of iterations in our foundation technology, which will take FAST and the industry forward. We would love to hear your thoughts.

If you're not sure how to provide feedback, here are a few things we'd love to hear from you about:

  • At which iteration does this toolkit become valuable to you? Why?
  • Is there any particular feature that excites you?
  • Is there any particular feature that you don't care about or won't use?
  • Would you be interested in the CLI automatically generating React wrappers for each component? Is there anything else we should generate? (Keep in mind we'll set you up with tests and a Storybook; I didn't go over all those details.)
  • Except tooling plans beyond the CLI, what did we miss?
  • What could you use from us to help you sell this to your company/team?
  • What would you build with this?
@EisenbergEffect EisenbergEffect added status:planned Work is planned docs:rfc Request for Comments (proposal) area:fast-foundation Pertains to fast-foundation epic A collection of features and tasks with a common objective. labels Apr 27, 2022
@EisenbergEffect EisenbergEffect pinned this issue Apr 27, 2022
@EisenbergEffect EisenbergEffect added this to the FAST Foundation 3.0 milestone Apr 27, 2022
@EisenbergEffect EisenbergEffect added status:in-progress Work is in progress and removed status:planned Work is planned labels Apr 27, 2022
@KingOfTac
Copy link
Collaborator

  1. For me, iteration 1 was the point that FAST was valuable. I stumbled upon the library by accident while researching design patterns and ways to abstract away the boilerplate of web components and it was immediately a game changer for me. I am however, very much looking forward to iteration 3 since if beyond that point existing APIs remain stable and aren't at risk of removal, it will mean I no longer have to do major refactoring each release unless I want to take advantage of newer features.
  2. There isn't a particular feature that stands out to me, more so the entire direction things are moving is what excites me, but if I had to pick something, I think the file and boilerplate generation is a huge plus.
  3. NA
  4. I think as long as wrappers are at least an option to be generated, it will only benefit a wider range of teams and I think it will also help speed up adoption of web components. I didn't see it on the list, but I'm assuming CEM is another thing that will be auto generated. If it's not, it would be a great addition.
  5. A plugin system for the CLI would be cool and open the door for a larger community-built ecosystem, which I think would offload a lot of work from the core team. This would let people fill their niche requirements quicker as well as open up possibilities for more experimentation in the community.
  6. My team is already on board, although I think it would have been an easier sell if the CLI was a thing a month ago. A time estimate for iteration 3 would also help.
  7. I am already building multiple libraries and design systems using FAST, both as personal projects and professionally. This will mostly increase my efficiency and decrease the time it takes to build new components since I am a one-person team both at home and at work.

@KingOfTac
Copy link
Collaborator

Another nice to have would be an option for the CLI to upgrade existing projects to the newer way of doing things.

@EisenbergEffect
Copy link
Contributor Author

Great feedback @KingOfTac ! @janechu Can you capture the requests for the CLI? I think we have most of this on our list but just want to make sure we don't miss something.

@CuddleBunny
Copy link
Contributor

What about a generator for a standalone component with a convention that when followed enables that community component to be pulled into the design system project from the CLI? I was thinking about #3960 and how I might distribute such a thing in this new world. Something like fast add-component https://github.com/CuddleBunny/fast-placeholder perhaps?

For starters this could just copy the component and register it like the CLI will do with the original fast components. A pipe dream future enhancement could be missing token detection where any design tokens that the new component uses which do not match any in the current project could be raised by the CLI where the end-user is asked whether they want to add that token or substitute with an existing token.

@EisenbergEffect
Copy link
Contributor Author

Great feedback. We had thought about supporting multiple project templates but this is more like being able to import a component template. Cool idea. Maybe we could support something like Gist, where you whatever files are in the gist get pulled in? /cc @janechu

@jattasNI
Copy link

I'm really excited about the builder pattern for templates. We've needed to extend fast-foundation templates by adding extra DOM elements for a few components. The only options have been sub-optimal: to do flaky string concatenation or to copy the FAST template into our design system and modify it. I think the builder pattern will make this a lot cleaner.

For extending and sharing CSS the builder pattern seems less urgently useful; this is because we've been able to leverage the existing appearanceBehavior pattern to share CSS between components and we haven't needed to extend FAST's CSS as often as we've extended templates. But organizing CSS by purpose as you show in your example looks really tidy so I won't be surprised if we find uses for that pattern too.

@EisenbergEffect
Copy link
Contributor Author

Great to hear the builders will be valuable for you @jattasNI. We'll definitely have the template builder work first. I actually spent some time prototyping it today. A little more time next week and I think I will have nailed down the details enough for us to move ahead with that. On the CSS side, there's a lot more to work out, so I think that will probably be the last piece, and we'll likely need a bit more time to experiment with it and see what works.

@levithomason
Copy link
Member

levithomason commented May 2, 2022

This is a well thought out write up. It sort of reminds me of the days when frontend tooling was iterating, browserify -> grunt -> gulp -> yeoman / parceljs / create-react-app / etc. type of progression.

The FAST vision for design systems seems more comparable to the yeoman level approach for building frontend projects. These things really helped accelerate and empower (especially smaller) teams to do bigger things. It also helped the need to spawn many projects rapidly.

The trade off with all management and automation systems is control over details in trade for velocity and ease of use. I think this will apply in a similar way to mostly smaller teams who don't necessarily have the resourcing or expertise to build all the details themselves, but want or need a system to fill the resourcing and expertise gap.

Whereas, I would expect for larger teams with 1) a single project to build 2) funding to manage the details and 3) strict specifications requiring exact details, it would be more efficient to directly implement the design system. Just as in the case of yeoman, parceljs, or other build system tools, with a large enough team and specific enough requirements there is always the need to "eject" from the tool and manage the system in full detail manually.

What are your thoughts on this?

@EisenbergEffect
Copy link
Contributor Author

It's certainly possible to eject or not use the template DSLs at all. No one is prevented from doing that.

However, the main point is that for the standard components, the core anatomies are largely the same with only a narrow set of variations per component. There are typically core functional structures that are required, if only to ensure that the accessibility model is correct. So, why rebuild that once it's worked out? Just use the DSL to specify the variation you want and be done with it. Hybrid models are also supported. So, if it works for ten of the components but not the eleventh, you can just build custom for the eleventh. It's another tool in the toolbox for any team looking to be efficient in building out a design system.

We've had two years of feedback from partners and customers around this, and paired it with a bunch of performance testing on template variations, so I'm feeling pretty good about forming an API that can handle all the variations and perf optimization scenarios we've seen with plenty of flexibility for things we haven't seen.

@levithomason
Copy link
Member

Thanks for the reply. It would be interesting to investigate shared tests for conformance as well. This is one step closer to the DIY end of the spectrum but still allows adherence. I'm thinking something similar to what singel tried to do with the single element pattern.

@EisenbergEffect
Copy link
Contributor Author

Absolutely. So, one of the cool things is that the build phase for each template has a validation step. This allows the anatomy to apply arbitrary rules to ensure functional structures, accessibility, or whatever else is desired.

@rajsite
Copy link

rajsite commented May 4, 2022

We tend to duplicate patterns from fast-components, like making a direction token for handling ltr-rtl, appearance behavior pattern like mentioned above, dozens of nitty gritty things like styles for button alignment, and even today I was trying to figure out if FAST has a pattern for z-order management which was closed because of the change in direction.

Some of these problems are going to need to be solved for any design system built on FAST and (at least speaking for us) we are looking to FAST to find the good patterns for localization, accessibility, and handling issues we expect are common like the z-order management.

Having the fast-components as the reference package for components that "do things pretty well" and is worth borrowing ideas from has been useful. It looks like the intention is to do away with that component library from the source tree: #5853. That also intends to remove the component explorer which is a valuable compare point when testing our fast-foundation based components against a "known good" implementation for filing issues.

  1. Would there be an alternative "reference implementation" or something where common problems like these are actively solved for a "gold standard" / "pretty good" component library built on FAST?
  2. If the kind of patterns discussed above are considered higher-order outside the concern of fast-foundation then how can those be shared better? (I'd feel more comfortable knowing dozens of systems are solving ltr-rtl styling or z-order issues similarly across many varied design systems)

@chrisdholt
Copy link
Member

Thanks for the feedback @rajsite - while #5853 does remove those packages, #5849 provides some insight into the "why" and reasoning behind that, as well as how we plan on continuing to provide some of those valuable out of the box implementations. If you haven't yet taken a look, hopefully that can provide some additional insight and background to the intent of 5853.

In terms of some of the OOTB patterns and whatnot, @bheston is working on somewhat of a "spec" to detail out plans for starting to abstract out some of the goodness that was previously delivered via fast-components for use in building design systems. While there's an open PR, we feel that a spec will help us scope and plan to scale for what's most urgent, and then what can be followed up with.

For instance, we believe that it's vital to get the adaptive color algorithms and recipes abstracted for use a la carte by folks. What is not known, is how much of an implementation should be brought in immediately and what other abstractions might be valuable. Over time, I could see including baseline resets as css partials, such as the base button styles, etc. Part of this also folds into the later stages of this proposal. Our hope with the spec is that it can help us draw clear lines for where those goodies live and what the best approach for abstraction and delivery is for each (IE, does it become a part of foundation, or does foundation take a dependency to provide that, etc...)

Wanted to get a quick response over - would love to hear more about what specifically has been valuable and how you've been leveraging so we can use that information to make sure our decisions help meet your needs long term!

@hawkticehurst
Copy link
Member

if only to ensure that the accessibility model is correct

@EisenbergEffect this actually just reminded me of another question I have about these proposed changes.

One of the long terms goals for the Webview UI Toolkit is to make sure that our accessibility model is aligned with that of VS Code core UI. We haven't done much work auditing the differences between the toolkit a11y model and VS Code core a11y model so there might not actually be that big of a difference (at least that's me hoping 🤞), but if possible it would be great to hear a bit more about how much flexibility (or "variations" as you put it) you hope the template DSLs will have?

The underlying need here is that we will always have to defer/align to whatever VS Code has defined as its core a11y model, but it would still be fantastic to take advantage of (or at least partial advantage of) any template DSLs that FAST might ship that ensure good a11y models.

Also forgive any apparent lack of knowledge/correct verbiage on the wider topic of a11y models, it's something I'm still ramping up on/learning about (thus the desire to take advantage of any expertise you all collectively have on the topic).

@EisenbergEffect
Copy link
Contributor Author

EisenbergEffect commented May 10, 2022

Great question @hawkticehurst. I feel pretty confident that you'll be able to get what you want. The template DSLs will be pretty flexible, and as always, we want to work with the community to refine these over time. So, if there's something blocking in a particular component DSL design, then we're always open to find a way to fix that. I think early feedback will be key here. To that end, let me just show some real code from my first prototype.

Here's what some code to produce an AccordionItem would look like, to setup the same default template structure that we have today:

AccordionItemAnatomy.define()
        .anatomy(item =>
            item
                .heading(heading =>
                    heading
                        .button()
                        .startSlot()
                        .endSlot()
                        .icons(icons =>
                            icons
                                .expanded({ fallback: "icon..." })
                                .collapsed({ fallback: "icon..." })
                        )
                    )
                .defaultSlot()
        ).build();

This creates an anatomy with a heading area that contains the expand/collapse button, with a start and end slot, as well as custom icons for both expanded and collapsed states. It also has a default slot where the item's content will be rendered. When you call build, the API returns a strongly typed ViewTemplate that can be used with anything that matches the interface for an AccordionItem. Of course, by pairing that with the AccordionItem base class and your own styles, you can very quickly build a production-ready standard AccordionItem component.

Now, imagine you want to change the AccordionItem anatomy a bit. Here's another code sample from my prototype that shows a different configuration:

AccordionItemAnatomy.define()
    .parts({
        button: "control",
        region: "content",
    })
    .anatomy(item =>
        item
            .heading(
                heading =>
                    heading
                        .html`first`
                        .button()
                        .startSlot({ name: "prefix" })
                        .html`last`
                )
            .defaultSlot()
    ).build();

This anatomy is quite a bit different. It has a heading area, but inside that there's some custom html before and after the expand/collapse button. That could be any HTML you want, including using FAST HTML Directives and bindings. Then notice that we only have a start slot and that we've renamed it to be "prefix" instead of "start". We also don't have any icons or their associated slots. Of course there's a default slot for item content as well. Besides customizing the anatomy, we've also customized the part names by renaming the "button" part to "control" and renaming the "region" part to "content".

The accessibility model is baked into the standard parts of each anatomy. But what you can see here is that you could choose not to leverage a particular part if it didn't match your model, and you could instead replace just that part with custom HTML. We hope that won't be necessary for most folks, but it's something this type of API enables, which we couldn't really do before.

@EisenbergEffect
Copy link
Contributor Author

If anyone is interested in how the AccordionItemAnatomy class shown above is implemented, here's the code for that. Please bear in mind that this is early prototype code and will probably change a bit. That said, I'm pleased so far with how easy it is to build these anatomy classes based on core FAST primitives.

type AccordionItemParts =
    | "region"
    | "heading"
    | "button"
    | "heading-content"
    | "start"
    | "end"
    | "icon"
    | "expanded-icon"
    | "collapsed-icon";

export class AccordionItemAnatomy extends Anatomy<AccordionItem, AccordionItemParts> {
    protected begin() {
        this.html`<template class="${x => (x.expanded ? "expanded" : "")}">`;
    }

    heading(callback: (a: AccordionItemHeadingAnatomy) => void) {
        this.internals.anatomy(AccordionItemHeadingAnatomy, callback);
        return this;
    }

    defaultSlot() {
        const { part } = this.internals;
        return this.html`
            <div
                class="region"
                ${part("region")}
                id="${x => x.id}-panel"
                role="region"
                aria-labelledby="${x => x.id}"
            >
                <slot></slot>
            </div>
        `;
    }

    protected end(): void {
        this.html`</template>`;
    }
}

class AccordionItemHeadingAnatomy extends Anatomy<AccordionItem, AccordionItemParts> {
    protected begin(): void {
        const { part } = this.internals;
        this.html`
            <div class="heading"
                ${part("heading")}
                role="heading"
                aria-level="${x => x.headinglevel}"
            >
        `;
    }

    button(options?: Partial<SlotOptions>) {
        const { part, slot } = this.internals;
        return this.html`
            <button
                class="button"
                ${part("button")}
                ${ref("expandbutton")}
                aria-expanded="${x => x.expanded}"
                aria-controls="${x => x.id}-panel"
                id="${x => x.id}"
                @click="${(x, c) => x.clickHandler(c.event as MouseEvent)}"
                >
                <span class="heading-content" ${part("heading-content")}>
                    ${slot(Object.assign({ name: "button", fallback: "" }, options))}
                </span>
            </button>
        `;
    }

    startSlot(options?: Partial<SlotOptions>) {
        const { slot } = this.internals;
        return this.html`${slot(Object.assign({ name: "start" }, options), ref("start"))}`;
    }

    endSlot(options?: Partial<SlotOptions>) {
        const { slot } = this.internals;
        return this.html`${slot(Object.assign({ name: "end" }, options), ref("end"))}`;
    }

    icons(callback: (a: AccordionIconAnatomy) => void) {
        this.internals.anatomy(AccordionIconAnatomy, callback);
        return this;
    }

    protected end(): void {
        this.html`</div>`;
    }
}

class AccordionIconAnatomy extends Anatomy<AccordionItem, AccordionItemParts> {
    protected begin(): void {
        const { part } = this.internals;
        this.html`<span class="icon" ${part("icon")} aria-hidden="true">`;
    }

    expanded(options?: Partial<SlotOptions>) {
        const { part, slot } = this.internals;
        return this.html`${slot(Object.assign({ name: "expanded-icon" }, options), part("expanded-icon"))}`;
    }

    collapsed(options?: Partial<SlotOptions>) {
        const { part, slot } = this.internals;
        return this.html`${slot(Object.assign({ name: "collapsed-icon" }, options), part("collapsed-icon"))}`;
    }

    protected end(): void {
        this.html`</span>`;
    }
}

@EisenbergEffect
Copy link
Contributor Author

Update: Foundation Iteration 4 is committed to our main branch and released in beta. The CLI does not yet generate components based on this version, but that work is in progress now.

@sonntag-philipp
Copy link

  • At which iteration does this toolkit become valuable to you? Why?

From iteration 1 the FAST Foundation is really a time saver just as it is for now and it will just improve in the future.

  • Is there any particular feature that excites you?

The ability to manipulate the anatomy of templates. This can be really useful in combination with the correct styles to create comprehensive and intuitive components.

  • Is there any particular feature that you don't care about or won't use?

No, I've thought that in the past of some features but they became really handy in many situations.

  • Would you be interested in the CLI automatically generating React wrappers for each component? Is there anything else we should generate? (Keep in mind we'll set you up with tests and a Storybook; I didn't go over all those details.)

I don't really care about React wrappers. Everything else is already included.

  • Except tooling plans beyond the CLI, what did we miss?

I couldn't see a direct guideline on how to document the components that are created. I think the Storybook will help, but I'm more looking for things like automatic documentation generation from JSDoc annotations.

  • What could you use from us to help you sell this to your company/team?

Good support and really good community interactions. But as far as I can see they are already extremely good. Thank you much for that! :)

  • What would you build with this?

First a small component library that will be used in some smaller projects. If the library works well in those smaller projects we want to make our web component library a standard for each new web project.

Aside from that I've just created an issue at the custom elements analyzer project concerning FAST Element compositions.
I currently use the analyzer to automatically create the documentation of my project which works kind of good.
If you don't already have it on your agenda it would be really nice to have this analyzer or similar tools available or at least good working as they really decrease the time that is necessary to create documentations.

@EisenbergEffect
Copy link
Contributor Author

EisenbergEffect commented Jul 26, 2022

Great feedback @sonntag-philipp ! On the documentation/analyzer front, we have plans around that as part of the CLI. We've recently done some work to generate custom-elements.json files from our foundation components and then to generate markdown documentation from that. We're looking at bringing this into the CLI so that the same can be auto-generated for any design system.

@sonntag-philipp
Copy link

Alright, I'm really glad to hear that!
Are you able to share some information of the parts that will describe the custom elements? Like, if they will be statically analyzed JSDoc annotations like the ones already used by the cem analyzer? I could also think of a completely different solution that uses TypeScript decorators or something else.
It's also fine if you can not say anything about that for now. I just want to be somewhat safe for the future in the ways I will create my component documentation :)

@EisenbergEffect
Copy link
Contributor Author

At present, the plan is to use the CEM analyzer. This is what we're using for our current foundation docs. We hope to have the CLI set this up for any design system projects as well.

@KingOfTac
Copy link
Collaborator

Until there is an issue specifically for tracking the template builder work for iteration 5. This brings up being able to customize shadow parts for some interesting styling scenarios, and likely would fit somewhere in that work in some shape. #6523

@yinonov
Copy link
Contributor

yinonov commented Dec 12, 2022

for iteration 4, the registration APIs that enabled overrides indeed was never used as we override (copy and edit) templates directly. so iteration 5 with breaking down template portions seem very promising. love the idea.

back to iteration 4, can you please clarify the registry: customElements?

export const designSystem = Object.freeze({
  prefix: "fluent",
  shadowRootMode: "open",
  registry: customElements
});

@EisenbergEffect
Copy link
Contributor Author

In the future the browser will support Scoped Element Registries. For vNext we're working to make sure the APIs are all ready for that when it arrives. So, for this configuration object, it has a registry option so that you can specify a custom registry for your system components. However, you can also just use the global customElements registry, which is what the setup here shows (and the only option today).

@yinonov
Copy link
Contributor

yinonov commented Dec 12, 2022

Thanks. was surprised to see it already baked in to the API

@yinonov
Copy link
Contributor

yinonov commented Feb 26, 2023

At present, the plan is to use the CEM analyzer. This is what we're using for our current foundation docs. We hope to have the CLI set this up for any design system projects as well.

trying to wrap my head around an issue where design system provider is used by consumers to customize the prefix.

in @sonntag-philipp cem issue

the following

/**
 * @tag qds-button
 */
export class Button extends FoundationButton { }

will output to JSON as

"tagName": "odui-button",
"customElement": true

but then consumers of a library prefixing their provider will no longer get intellisense over their <custom-button> tags.

is it a highly rare use-case?

@usrrname
Copy link

usrrname commented Mar 1, 2023

Up to this point, @microsoft/fast-foundation only provides you with pre-constructed templates for each component. That's great for most cases, but a few scenarios aren't possible. Examples include:
You can't add/remove/change slots.
You can't add/remove/change parts.
You can't add new HTML before, after, or around the existing markup.

Spot on! 🥇

This point is something my lean and mean engineering team is facing as potential maintenance burden if we are copying and extending component templates in order to:

  • build interactions or do conditional rendering in templates
  • dispatch custom events (mostly for analytics tracking in light DOM when/if components are used in marketing websites, for which there are sooo many at my work)

This is moreso commentary; I wish I had a solution to offer but I'm not smart enough to write a language yet 😅

@EisenbergEffect
Copy link
Contributor Author

Update: I'm currently working on tech to get us all the way to Iteration 6 and beyond. I'm taking a bit of a different approach to what is talked about here from an implementation perspective. The new approach is much more powerful and will enable many creative possibilities and collaborations. I've probably got another month or two of work to get it ready for a PR. Thank you for your patience.

@ThorFjelldalen
Copy link

@EisenbergEffect Any more updates on this new approach? This is a real cliffhanger, and I'm a little curious! 😄

@EisenbergEffect
Copy link
Contributor Author

@ThorFjelldalen I left Microsoft in November of last year. Since then, I haven't been able to contribute much to FAST. The work I was doing back in March was a side project and unfortunately had to be paused as it didn't align with what I needed to do for my current business.

@ThorFjelldalen
Copy link

Ah, I understand. Thanks for the quick answer!

@radix
Copy link

radix commented Nov 3, 2023

Okay, so... if EisenbergEffect is gone, what's the status of Fast? Is there any chance of this ambitious project moving forward? Is MS still putting any resources into this project? An update would be appreciated.

@chrisdholt
Copy link
Member

Okay, so... if EisenbergEffect is gone, what's the status of Fast? Is there any chance of this ambitious project moving forward? Is MS still putting any resources into this project? An update would be appreciated.

The project is still moving forward. I won't speak fully for @EisenbergEffect, he is still a valued part of the Steering Committee, and we just met together recently (last week). It's still moving forward and different people across Microsoft make up the core maintainers, as well as a few core people outside of MS. The work that has been done on FAST has always been an OSS effort and that continues to be the case as it will into the future.

@Falkicon
Copy link
Contributor

Falkicon commented Nov 3, 2023

Not only does Microsoft help maintain FAST but it is also a user of FAST. To use it, we needed a version of Web Components fully aligned with Fluent 2. Over the last year or so, that is where a lot of our focus has been centered and that effort is nearing completion to the point that we have a large set of Fluent UI Web Components available to use inside Microsoft and by 3rd parties. Once teams have a chance to onboard to these components, focus and investment should shift back toward FAST as teams need features and improvements.

@derekdon
Copy link
Contributor

derekdon commented Mar 5, 2024

Alright, I'm really glad to hear that! Are you able to share some information of the parts that will describe the custom elements? Like, if they will be statically analyzed JSDoc annotations like the ones already used by the cem analyzer? I could also think of a completely different solution that uses TypeScript decorators or something else. It's also fine if you can not say anything about that for now. I just want to be somewhat safe for the future in the ways I will create my component documentation :)

Might be of interest https://github.com/genesiscommunitysuccess/custom-elements-lsp

@janechu janechu unpinned this issue May 8, 2024
@janechu janechu removed this from the FAST Foundation 3.0 milestone May 28, 2024
@janechu
Copy link
Collaborator

janechu commented May 29, 2024

Closing this as Foundation is being deprecated, for details please refer to #6955.

@janechu janechu closed this as completed May 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:fast-foundation Pertains to fast-foundation docs:rfc Request for Comments (proposal) epic A collection of features and tasks with a common objective. status:in-progress Work is in progress
Projects
Status: Done
Development

No branches or pull requests