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

Restyle Components #284

Open
superstructor opened this issue May 6, 2021 · 3 comments · May be fixed by #250 or #288
Open

Restyle Components #284

superstructor opened this issue May 6, 2021 · 3 comments · May be fixed by #250 or #288

Comments

@superstructor
Copy link
Contributor

superstructor commented May 6, 2021

Overview

Resolve all re-com styling issues.

Problems

The styling of re-com is currently a complex layer of:

This leads to issues including:

Goals

  • Colocation We want the code for CSS to be adjacent to the CLJS code for components which use them.
  • Simplicity i.e. remove bootstrap.css, re-com.css and inline styles layers.
  • Efficiency in repetitive performance critical code (e.g. v-table row renderers) we want classes, not inline styles. Some benchmarks indicate this should be faster
  • Skinning/Theming We want to allow re-com library users to skin/theme re-com components in different ways:
    • Full replacement of all styles (e.g. use Material Design)
    • Full replacement of styles for a particular component (e.g. datepicker)
      • (Subset) Full replacement of styles for a particular component in a particular state (e.g. disabled?)
    • Just tweak the defaults a bit by changing a single property or amending a missing property (e.g. add some bottom margin).
  • Shaking We want unused styles to be omitted in production builds e.g. if datepicker is not used the code for generating the CSS should not be included in the .js file and the <style> tag should not be registered at runtime.

Analysis

  • A component is made of a tree of HTML elements which we call parts
  • A component has some state related to:
    • its visual appearance
      • Disabled?
      • Dropdown open?
      • At any time, a datepicker is displaying a certain month
    • the model value being represented
      • InputText contains copies of model internally
      • what date is today?
      • what is the selected date?
  • component state arises via:
    • passed in via args (eg disabled?)
    • caused by user actions, clicks, mouseover etc (to select a date, or open a dropdown). Some of these state changes are communicated
    • Internal code; e.g. for loop over dates in a month results in ‘state’ of a date per day
  • A part’s existence is often dependent on state. For example, the “open” state for a dropdown causes the popover part to exist, which actually shows the dropdown.
  • A part’s styling (classes) is often dependent on component state.

Proposal

Replace all existing styling with Garden and Spade.

Order of Components

First we will do dropdown (i.e. medium complexity) then we will do datepicker (i.e. most complex) to immediately verify our working model is good.

Process For Each Component

1. State Chart Model

First, we need to understand the abstract model of all the possible states of a component in order to be able to implement a way to style those states.

Write a state chart in PlantUML and embed it in a GitHub Markdown file named after the component (e.g. src/re_com/datepicker.cljs would be docs/datepicker.md).

E.g. for the dropdown component it might have a disabled? and an enabled? state, and in the enabled? state it might have states like open?, focused? etc which fundamentally impact the appearance of the component. Or in other words, what states exist that might need to have different styles or different classes associated with those states ?

Make a PR.

2. Parts Model

Second, we need to document all the possible parts (see 'Parts' at bottom of page) of a component.

Although the PlantUML component diagram is talking about a different type of component we could possibly use the 'Grouping Components' syntax as a rendering of part hierarchies. Otherwise, we could just represent it in pure Markdown. Either way, add to the Markdown file created in Step 1.

Make a PR.

3. Restyle Component

  • Disable re-com.css and bootstrap.css (e.g. by commenting out <link.. tags in index.html)
  • Remove all inline styles from the component
  • Re-implement styles in Garden with Spade's defclass. :compose common styles (e.g. flex-child-style.
  • Add an unstyled? parameter to the component that removes all classes and styling that defaults to false.

When the component is the same with unstyled? false as the existing styling, make a PR.

4. Expose Parts and State for Styling

  • Ensure all documented parts are exposed via the :parts API
  • If the :class or :style value in :parts is fn? then call it with a map of the current component state (e.g. {:disabled? true :open? false :today date}) and use the return value of the fn
  • Watch Crafting Stateful Styles with State Machines by David Khourshid | CSSConf BP 2019
  • Add data attribute to the component markup for all states (e.g. <div class="..." data-state="disabled">)

Make a PR.

@sunng87
Copy link

sunng87 commented Nov 24, 2021

This sounds super promising. Is there any progress or opened pull request for this work?

@superstructor
Copy link
Contributor Author

superstructor commented Dec 1, 2021

@sunng87 Thanks. @MawiraIke has done some initial proof of concept branches for a couple of components but we're still finalising the design prior to starting actual impl. @MawiraIke is funded to work on this after he has finished with some higher priority re-frame-10x issues.

@BnMcGn
Copy link
Contributor

BnMcGn commented Sep 27, 2022

Hi. I'm interested in using re-com with tailwind. It looks like you've all been busy with other things, so I've taken a peek at the code and I have a proposal:

Move all of the inline class and style data from the components to a central map structure stored in css.cljs file. Keys will be the name of the component. Values will mirror the structure of the 'parts' parameter.

Create a utility to merge the stored css settings with the user supplied :class, :style and :parts parameters.

Obviously the css has to, on occasion, respond to the state of the component. So the css structure will be able to contain functions as well as strings. The css merging utility will take a map of options to pass in when it finds a function in the structure. The function can then return the appropriate classes or styles based on the options.

Someone who wants to adapt re-com to tailwind or something similar should only need to swap out the central css storage structure.

Foreseen difficulties:

  • Doesn't involve garden/spade. I haven't had time to investigate these tools and I don't understand them, but imagine they can still be integrated.

  • CSS won't be near the components that use it.

  • Shaking out the unused stuff might not work too well.

As a potential solution to #2 and #3 above, instead of a single central data structure, each component could declare its own default class/style structure much like is already done with -parts-desc and -args-desc.

To repurpose re-com for other css frameworks, you'll have to hunt down and override all of these *-css-desc declarations.

So before I start coding, am I missing anything obvious? I've only read through a few of the re-com source files. Are there any tricky corner cases that I might have missed and should know about before I jump in with both feet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants