microsoft / fast

The adaptive interface system for modern web experiences.
https://www.fast.design
Other
9.28k stars 595 forks source link

The Future of FAST Foundation #5901

Closed EisenbergEffect closed 5 months ago

EisenbergEffect commented 2 years ago

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:

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:

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:

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:

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:

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:

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:

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:

KingOfTac commented 2 years ago
  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 commented 2 years ago

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

EisenbergEffect commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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: https://github.com/microsoft/fast/pull/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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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>`;
    }
}
KingOfTac commented 2 years ago

Took a minute to digest the new syntax, but it looks great.

hawkticehurst commented 2 years ago

@EisenbergEffect thank you for the write-up/explanation, this helps a lot!

I'm excited to see how this evolves, but it all looks very promising. I especially appreciate the ability to include arbitrary HTML as a sort of backdoor for the whole process if it's needed.

Also as time/energy permits, I would love to see more examples of this API as it evolves over the coming months.

EisenbergEffect commented 2 years ago

Next week I plan to start the work to move foundation to Iteration 4. That won't include the anatomies but it will get us through the breaking changes needed to enable that in the next iteration. I'm very much looking forward to that milestone. Once that's in place we can start building anatomy APIs in an incremental fashion for each component without introducing breaking changes.

In other words, I hope you'll be seeing more of this soon 😄

EisenbergEffect commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 2 years ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

yinonov commented 1 year ago

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 commented 1 year ago

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:

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

EisenbergEffect commented 1 year ago

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 commented 1 year ago

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

EisenbergEffect commented 1 year ago

@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 commented 1 year ago

Ah, I understand. Thanks for the quick answer!

radix commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 8 months ago

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 commented 5 months ago

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