opral/monorepo

settings web component for inlang apps

Closed this issue ยท 32 comments

Context

Just like opral/inlang-message-ui-components#16, we need a settings web component:

  • unified settings experience across inlang apps
  • saving inlang app developers' resources by offering embeddable ready to use component
  • (an in-app settings view will always be better than redirecting to manage.inlang.com)

Features

Must have

  • #2250
  • slot for custom settings cc @opral/inlang-ide-extension
  • responsive
  • validation (take JSON schema of settings.json and module settings schema?)

Maybe

Out of scope

  • ?

Additional information

We used to believe that we should build a site like manage.inlang.com that offers a settings view. Apps can open the external site and spare themselves from building a settings view. However, linking to an external site is a large user flow breaking point. Instead of developing one website with a settings view, we can invest the resources into a web component that apps can embed.

I am unsure about:

  1. which group should develop and maintain the web component. Giving @jldec the job seems wrong given that we have @NilsJacobsen @NiklasBuchfink and @felixhaeberle on the team with design engineer experience. A large part of the settings view is a) embedding into apps and b) user experience.
  2. If module management should be part of the settings or a separate "tab". In any case, we can push updates to the component incrementally.

I think @NilsJacobsen would be a fit:

  1. Its a cross app thing like the inlang website aka marketplace.
  2. He is responsible for manage and this would replace it.
  3. Felix and I already have one large and one small app. This way Nils has the website as "large app" and the web component as (hopefully) "small App".

Why not I am happy to work on that. I guess the timeframe is to get an MVP ready until we get the getting-started right?
Gonna explore a bit and select requirements for the component.

**Notes from convo with @NilsJacobsen **

  • main point: how to expose styles from host app
  • awesome: settings.json JSONSchema and module settingsSchema (also a JSONSchema) can be used to build the settings UI

Insight: The entire component is "just" a rendering engine for JSON SChemas. APp settings, module settings and project settings can all be rendered from a JSON schema.

  • make project read only -> no implicit behaviour for apps
<inlang-settings 
  project={project} 
  onSettingsSet={(settings) => project.setSettings(settings)}
  appSettingsSchema={appSettings}
  onAppSettingsSet={(settings) => {}}
>
</inlang-settings>

if there is a problem & the settings component can't resolve the JSON schema, we can always fallback to "Edit in settings.json" โ€“ it should then only be clear which settings source file. The one from VS Code, the inlang one etc.

Bildschirmfoto 2024-02-20 um 14 51 52

First exploration into styling and other questions:
https://www.loom.com/share/b3835c31ed3745a9832754bd023cbbd0

TL;DR

  • I built a quick prototype with lit, vite, storybook, and shoelace (styled with CSS variables)

Questions:

  • Do we want to have versioned releases for UI? (Yes)
  • Does something speak against storybook? (I like it)
  • Waht do you think about the CSS variables approach for style overwrites? (I like it)

Do we want to have versioned releases for UI? (Yes)

Yes.

Create a separate package for the settings component that is released like the other NPM packages.

Does something speak against storybook? (I like it)

Nothing speaks against storybook.

Waht do you think about the CSS variables approach for style overwrites? (I like it)

Yes, good. Don't expose shoelace variables, though/have your own naming convention (ideally the same one as the pattern editor will have).

Further notes

  • i would name the package "settings-component" and not nest it in "shared-ui". let's strive for a flat package structure to avoid naming convention discussions. (nesting in versioned-interfaces was a mistake :D)

Notes after a call with @felix:

  1. no need to ship themes. Apps need to adjust the style to their env anyways

  2. thus, ship the component with "base" style that can be overwritten by the app. Base style = for example no colors, clear that the styling is not complete BUT usable

  3. variants like button small, large etc seems redundant.

@NilsJacobsen Call me tomorrow so we can clarify on a few thoughts I had after seeing your loom what led to my response loom & a call with samuel about how we should build & enforce the right things to make our life really easy by not compromise on the goal we want to achieve (highly flexible styling, unified UX).

In short: (what samuel said above and) the right set of CSS variables with near unstyled defaults make variants & themes obsolete.

Moreover, the less we have to maintain = the better & we do not have to keep syntax & theme(s) in sync all the time.

Also, let us have a brief chat about the beloved topic of icons.

please just agree to ship icons with the component. this will ensure that:

  • less maintenance for apps
  • unified icons for settings! (imagine app 1 having a different icon than app 2 for the same setting)

at the cost of:

  • icons that might not align with the rest of the app (can be improved in a follow up v2)
  1. no need to ship themes. Apps need to adjust the style to their env anyways
  2. thus, ship the component with "base" style that can be overwritten by the app. Base style = for example no colors, clear that the styling is not complete BUT usable

Apps have full control over styles, but if they want to take the base styling with small adjustments there is no need to define 300 CSS variables. Problem: If I build an app with dark styling I basically need to manually invert 300 tokens, instead of importing the dark base styling and change only the primary color. That was the idea.

We can also remove the dark styling for now. Because the ide extension can not set static dark themes, because it is based on a user-based theming.

Our UI will use a lot of shoelace components under the hood. The easiest way to style them is to change their css variables. So why not just using the same system with a inlang-token wrapper. That makes my components so much easier.

Let me explore how to make the wrapper.

the right set of CSS variables with near unstyled defaults make variants & themes obsolete.
variants like button small, large etc seems redundant.

Well the idea was to use CSS variables. They are used as a theme at shoelace. Where is this variant thing coming from?

If I build an app with dark styling I basically need to manually invert 300 tokens, instead of importing the dark base styling and change only the primary color. That was the idea.

Q: This will never meet the apps reqs in terms of styling. Think about it: If I have an app which supports dark mode, would I really want the settings component dark theme?

A: Likely not, because this theme works with its own shades of black which is not working with my own design which ships with other shades of black. There will always be the case of adjusting variables in order to unify the styling.

Real life example

Could Fink use this dark theme? No, Fink doesn't even support dark mode yet. Does the VS Code extension need it? No, there is no such thing as "dark theme" in VS Code, but 1000+ dark themes all with slightly different colors where I want the settings component to match every time to not look broken/alien.

Proposal

Ship the component with near unstyled defaults (a bit like a blank html page but a bit more beautiful) that it doesn't look broken and let the app handle the design from a-z.

@felixhaeberle You mean, just take the base color and text color from the parent? This way it always fits the theme. (black on white in Fink, white on gray in VS code default theme, etc)

Call with felix:

  • we don't do the dark theme for now because we don't have a use case
  • we provide base set of tokens (as a base theme)
  • app users can override them

Update:
https://www.loom.com/share/543ed27d63a74608a5134718b56e9ae2

Two questions:

  • Should we name the component <inl-settings/> while the package is named @inlang/settings-compoenent?
  • Samuel can clarify the versioned interface mistake?

Should we name the component while the package is named @inlang/settings-compoenent?

Don't abbreviate. Name it <inlang-settings>. The package name and HTML tag name prob do not need to align.

Samuel can clarify the versioned interface mistake?

Nesting is wrong. Just have a flat packages registry. Same problem with namespaces for keys.

Update settings component:
https://www.loom.com/share/25cac383d4164fbdb4c26018d7697971?sid=5de62c4e-19d2-4bf6-a00e-949d0574c6c8

TL;DR
-> Deriving the input fields from the schema and filling it with the settings was pretty straight forward

image

Save behaviour:

@NiklasBuchfink @felixhaeberle @martin-lysk

What do you expect from an input field in the settings?

1. Autosave

Pro:

  • easy
  • we need to indicate that a save happened (like the check mark in fink)

Cons:

  • We need to validate after each keystroke (which could also be nice)
  • If we save after every keystroke, that should not affect a reload of the app (might depend on apps)

2. Save per input

Pro:

  • Known pattern
  • Explicit
  • Less frequent updates callback

Cons:

  • not that fast

3. Save the whole form

Pro:

  • Easy to maintain
  • Known pattern
  • Explicit (not as explicit as the save per input)
  • Less frequent update callbacks

Cons:

  • Save button placement is crucial

Takeaways from GH UI Guide -> https://primer.style/ui-patterns/saving

  • Autosave only for toggles, single selects and menus
  • Save should always be in view
  • Group save must be obvious
  • Don't mix auto-saving controls and explicitly saved controls
  • Alert unsaved changes
  • Never hide or disable save buttons

Proposal: Let's start with save whole form

Proposal: Let's start with save whole form

Agree. Have a save button that is always visible (not at the very bottom!) and nudges users to save their changes.

Proposal: Let's start with save whole form

I agree. To fix the position problem:

  1. Make the button sticky at the top or bottom of the window.
  2. Add a dialog before closing/moving away without saving. (like on Twitter with tweet drafts)

Sketch for 1, but the button on the right:
image

@NilsJacobsen i propose to design the settings UX in figma before you code stuff. you can align everyone much better if you show what the resulting UX will be rather than showing "i coded this".

https://www.loom.com/share/9894ef2e87c94160b0afd0c92ba41d7a?sid=3963f6f6-2899-4ff7-aab0-d8b161cc7ee6

image

I want to go in that direction.

Quick feedback:

  1. Most apps won't need the headline / context: "Project Settings" and subheadline are therefore to be put by apps if they needs this in the context.

  2. Would be cool to have full control over the width of buttons and inputs, I likely want to have all input fields 100% width in really small widths (or you account for that ootb)

  3. The save overlay is a pattern not being used by the vscode extension for example and therefore inherits a UX the user might not be familiar with.

  4. Extending 3, I don't know how to style this element (overlay) with elevation from the background, because the "grey" color you anticipated for it already in the design mockup doesn't reliably exist in the vs code coloring hierarchy.

There are a few minors still in addition to that, but we can clean this is development.

@NilsJacobsen can you share the link to the exaclidraw? It's likely easier to comment directly on the excalidraw.

I have several questions about the UX flow:

  • what happens if a user clicks "add module"?
  • should module management be in the scope of v1?
  • should module management be split in lint rules and plugins? yes, they are "modules" under the hood but does a user need to know that? aren't they looking for plugins and lint rules?

+1 for not exposing the concept of modules to users in the settings display but rather split them into the logical blocks/types = plugins & lint rules.

how is this currently being solved at manage.inlang.com ?

separate plugin and lint rules

yes

Remove modules for v1

-> + 1

save overlay?

let's have a quick chat @felixhaeberle

Decision in call with @NilsJacobsen @felixhaeberle:

Design the settings component as a dedicated "page". The tradeoff to design the component "embedabble" (to be rendered in the page of an app) is too high with questionable UX outcomes.

The tradeoff to design the component "embedabble"

This tradeoff should be accounted for / fixed sometime in the future but for a v0, the approach of introducing a component based save UX is tolerable.