trousers icon indicating copy to clipboard operation
trousers copied to clipboard

Trousers v4 - An idea

Open danieldelcore opened this issue 5 years ago • 4 comments

Improvements ✅

  • Leaning into the CSS prop
  • Zero config SSR becomes possible
  • Modifier predicates are passed into the underlying element
  • No longer dependant on hooks
  • Dynamic properties can be applied to css vars to avoid remounting styles
  • Less dependency on React

Challenges ❌

  • Extending proptypes in typescript for basic elements
- import { useStyles } from '@trousers/core';
- import styleCollector from '@trousers/collector';

+ /** @jsx jsx */
+ import css from '@trousers/core';
+ import jsx from '@trousers/react';

- const styles = props => styleCollector('button')
-    .element`
-        background-color: ${theme => theme.backgroundColor};
-    `
-   .modifier('primary', props.primary)`
-        background-color: #f95b5b;
-    `;

+ const styles = css('button', `
+        background-color: ${theme => theme.backgroundColor};
+   `)
+    .modifier('primary')`
+        background-color: #f95b5b;
+   `
+    .modifier('disabled')`
+        background-color: red;
+   `;

const Button = props => {
-    const buttonClassNames = useStyles(styles(props));

    return <button 
-      className={buttonClassNames}
+      css={styles}
+      primary={props.primary}
    >
      {props.children}
    </button>;
};

export default Button;

Themes should now be mounted as classnames of css vars. Rather than depending on costly react context, we'll apply themes via a classname and css vars. The themes will be mounted to the head just like any other style.

For example:

const theme = {
 primary: 'red',
 secondary: 'blue,
};

is mounted as:

.theme-somehash {
 --primary: red;
 --secondary: blue;
}

which is applied to a button like so:

/** @jsx jsx */
import css from '@trousers/core';
import jsx from '@trousers/react';
import createTheme from '@trousers/theme';

+ const theme = createTheme({
+   primary: 'red',
+   secondary: 'blue,
+});

const styles = css('button', `
-        background-color: ${theme => theme.backgroundColor};
+        background-color: var(--theme-secondary);
   `)
   .modifier('primary')`
+        background-color: var(--theme-primary);
  `;

const Button = props => {
    return <button 
      css={styles}
      primary={props.primary}
+      theme={theme}
    >
      {props.children}
    </button>;
};

export default Button;

danieldelcore avatar Sep 24 '20 06:09 danieldelcore

Complete example:

/** @jsx jsx */
import css from '@trousers/core';
import jsx from '@trousers/react';
import createTheme from '@trousers/theme';

const theme = createTheme({
  default: 'blue,
  primary: 'red',
});

const styles = css('button', `
       background-color: var(--theme-default);
   `)
   .modifier('primary')`
       background-color: var(--theme-primary);
  `;

const Button = props => {
    return <button
      css={styles}
      primary={props.primary}
      theme={theme}
    >
      {props.children}
    </button>;
};

export default Button;

danieldelcore avatar Sep 25 '20 00:09 danieldelcore

Precursor integration notes:

  • Themes feel a bit disconnected from each other.
  • Where are themes coming from? How is a global theme different to a local theme?
  • Maybe we can prefix themes some how? Or use a $ similar to stitches
  • Can we potentially provide a helper that allows us to do typechecking + default values?

danieldelcore avatar Jan 23 '21 09:01 danieldelcore

Could the macro support this?

const styles: CSSProperties = {};

    if (start) {
        styles.gridColumnStart = start;
    }

    if (end) {
        styles.gridColumnEnd = end;
    }

    if (span) {
        styles.gridColumnEnd = `span ${span}`;
    }

    return <div css={styles}>{children}</div>;

It could, potentially with an additive API. styles.append('element', 'gridColumnEnd', 'span 5')

danieldelcore avatar Jan 23 '21 09:01 danieldelcore

Macro dynamic interpolation edge case:

css('Stack', {
            display: 'grid',
            gridTemplateColumns: 'repeat(1, 1fr)',
            gap: `var(--space-${gap})`, // We need to be able to detect this 
            width: '100%',
        })

danieldelcore avatar Jan 23 '21 09:01 danieldelcore