Giter Site home page Giter Site logo

morfeojs / morfeo Goto Github PK

View Code? Open in Web Editor NEW
36.0 4.0 4.0 22.05 MB

Morfeo is a build-time CSS-in-TS solution for the next level theming, with the minimum amount of shipped CSS.

Home Page: https://morfeo.dev

License: MIT License

JavaScript 0.60% TypeScript 64.40% HTML 0.55% CSS 34.45%
design-system typescript css-in-js react framework-agnostic morfeo morfeo-js javascript build-time css

morfeo's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

morfeo's Issues

wrapper of parsers and theme

Problem

Morfeo is based in 2 singletons, theme and parsers, the first is used to handle and provide the theme and the second one to parse it and generate styles. Even if it's not hard to understand this concept, most of the times the only methods we need to use of these 2 singleton are:

  • theme.get to get the current theme
  • theme.set to update the theme
  • parsers.resolve to parse the style object with the theme and generate valid css-in-js objects.

Solution

My idea is to create another singleton morfeo that exposes only these methods:

  • getCurrent: to get the current used theme
  • addTheme to add a new theme with a given name (for example "light" or "dark")
  • useTheme to use one of the previously added themes
  • setTheme same as theme.set
  • getTheme same as theme.get
  • getThemes to get all the added themes
  • resolve same as parsers.resolve
    With this solution, the new singleton morfeowill have an easy way to handle multi-theming and, most importantly, will expose only the API that 99% of the time we need; theme and parsers singletons will still be available for advanced usage for example add new parsers or easily get single slices or values from the theme.

Example

import { morfeo } from "@morfeo/core";
import { lightTheme, darkTheme } from "./themes";
 
morfeo.addTheme("light", lightTheme);
morfeo.addTheme("dark", darkTheme);

morfeo.useTheme("light");

const style = morfeo.resolve({
    py: "m",
    gradient: "primary"
});

// will be the current theme, `lightTheme` in this case
const currentTheme = morfeo.getTheme();
// optionally you can pass the name of the theme you want to get
const otherTheme = morfeo.getTheme('dark');

Originally posted by @mauroerta in https://github.com/VLK-STUDIO/morfeo/discussions/59

morfeo's hooks should work without wrapping the app with `MorfeoProvider`

The provider MorfeoProvider exported by in @morfeo/hooks is a provider that should be applied when Morfeo is used in a React environment, but it should not be mandatory.
The provider in fact subscribe the application to theme changes, but the providing of the theme is made by Morfeo (@morfeo/core) anyway.

To Reproduce
Use hooks like useTheme or useStyles without wrapping the app with MorfeoProvider

  1. set one or multiple themes
  2. use useTheme inside a component and log the given object
  3. See error (it logs an empty object)

Expected behavior
It logs the current theme

Dev Tool re-design

The current dev tool is mainly a proof of concept, now we know that it's possible to visualize the theme and the components inside it and we want to push it to a new level!

Motivation

The goal is to build a tool useful for both designers and developers to easily see the design language; Other then this, the dev tool creates a way to visualize the theme and the components automatically, making the process of documenting the design system easier for the developers. For basic components (such as Buttons, Containers or Typographies) it can even replace tools like storybook.

Features

The following are the list of features we will like to have in the first version of the dev tool:

  • Visualization of all the theme slices
  • More details for Theme Components (like complete style preview or code snippet for common libraries/frameworks like React or Svelte)
  • Multi theming

Contribution

Gabriele and Luca already start to think how the dev tool will look like, whenever the design will be ready we will start the developing process, anyway if anyone has some idea how the dev tool can be done please leave a comment or feel free to send us a message on our slack channel

Meta information in ComponentConfig

As suggested by Luca it could be useful to have an additional attribute inside the ComponentConfig with additional information of the component such as a description, a category, tags or other meta infos that can be used to document the component or by external plugins.

These information should not be used to stylize the component but only to get additional informations.

To enable this feature it's enough to only edit the type ComponentStyle inside the file packages/spec/src/types/components.ts and add a new attribute called meta:

type ComponentStyle<Props extends Style = Style> = {
  tag?: string;
  style: Style;
  props?: Props;
  meta?: Meta;
};

This Meta type could be or any object type Meta = Record<string, any> or a more strict interface like:

interface Meta {
   name?: string;
   description?: string;
   category?: string;
   tags?: string[];
};

web-extension - Add a devtool config object to meta and use it to better present components inside the web extension

Is your feature request related to a problem? Please describe.
Sometimes the style of a component couldn't be enough consistent to present it in a nice way inside the web extension.

Describe the solution you'd like
In these cases, it could be needed to add an additional style to the component, in order to present it in a better way inside the web extension. We can add a devtoolConfig property inside meta to define these additional behaviours.

Example

const Flex = {
  tag: 'div',
  style: {
    display: 'flex'
  },
  meta: {
    devtoolConfig: {
      label: 'Flex Div',
      style: {
        size: '100px',
        bg: 'gray.light'
      },
      background: 'white',
    }
  }
}

Discord link inside READMEs

The discord link inside the README should point to https://discord.gg/5hbsKMBRBh which is the invitation link to join the server.

Publish workflow

Describe the bug
After producing a new release, the github workflow publish starts and automatically publish the new versions of the packages.
Even if all the packages will be published, the workflow fails because inside the package @morfeo/cli a file called
packages/cli/tsconfig.tsbuildinfo and this will result in uncommited changes.

To Reproduce
Produce a new release

  1. Go to 'Releases'
  2. Click on 'Draft a new release'
  3. Fill the form
  4. add the release
  5. Wait for the publish and see the error

Expected behavior
The worflow ends without any error.

Screenshots
image

โœจ chore: publish new version

โœจ chore: publish version 0.5.0

This version will include the following updates:

  • @morfeo/preset-default: updates to colors, shadows and borders
  • @morfeo/hooks: new hook useCurrentTheme (documentation here)
  • @morfeo/core: breaking change the the core api:
    1. morfeo.useTheme renamed into morfeo.setCurrentTheme
    2. morfeo.getCurrent renamed into morfeo.getCurrentTheme

Add a default set of components to `@morfeo/preset-default`

It could be useful to have inside the package @morfeo/preset-default not only a default set of colors, borders or spacings, but even a set of components.

I think a good set of components should cover all the possible components that can style a MardDown page, in this way we can start to make custom themes for docusaurus or markdown renders.

Maybe it could be a god idea to first try to write components and test them inside the web-sandbox, then we can try to use it even inside the svelte-sandbox so we can be sure they are working fine in different frameworks / libraries.

I think we have to think about how to include the default set of components in a way that can be used even inside react-native, I was thinking about something like styled-components that exposes in a single package the sub-package styled-components/native, so for example we can have:

Web environment

import { lightTheme } from "@morfeo/preset-default";
// lightTheme: includes components made for the web environment, so they can include:
// media queries, pseudo classes or pseudo elements
// and the tags are related to html elements like `div`, `span`, `button` or `input`

Native environment

import { lightTheme } from "@morfeo/preset-default/native";
// lightTheme: includes components made for the native environment, so they DON'T include:
// media queries, pseudo classes or pseudo elements
// and the tags are related to native components like `View`, `Pressable` or `Text`

Folder structure

Move apps from the folder apps to a folder examples and rename the apps to:

current name rename
web-sandbox react
native-sandbox react-native
svelte-sandbox svelte

Also, move benchmarks from apps/benchmarks to benchmarks.

I think in this way it will be more clear what apps really are (examples), also these example can be probably moved in a separate repo whenever Morfeo will be stable.
Benchmarks, instead, it's something that we are currently using to always monitor the impact of new features.

Extends a component variant style with another component variant

Describe the bug
Morfeo should allow to extend a component style (or a variant component style) based on another component or component variant, for example:

const theme = {
  ...rest,
  components: {
    Component: {
       style: {
         color: 'text',
       },
       variants: {
           one: {
              style: {
                color: 'primary',
              }
           },
           two: {
               style: {
                  componentName: 'Component',
                  variant: 'one',
                }
            },
        },
    },
  }
};

In this case the style of the variant two of the component Component should be exactly the same as the variant one.
This is not happening right now, this exact style will generate { color: 'text' } instead of { color: 'primary' }`.

To Reproduce
Apply a theme with a component configuration as the example before

  1. Run morfeo.setTheme with this theme
  2. Run morfeo.resolve({ componentName: 'Component', variant: 'two' })
  3. See error

Expected behavior
Should return the same style as morfeo.resolve({ componentName: 'Component', variant: 'one' })

[web-extension]: Fonts-related slice have wrong font-families

Describe the bug
Fonts-related slice have a wrong font-family, this problem is caused by the fact that the passed property to the detail is not fontFamily but font.

To Reproduce
Steps to reproduce the behavior:

  1. Go to Any Font-Related slice like: Fonts, Font Sizes, Letter spacings and so on...
  2. See the error
  3. Click inside any detail
  4. See the error

Expected behavior
The font family is the one read from the current website/application that the web-extension is documenting.

wrap theme and parsers singleton into a single unified interface

Problem

Morfeo is based in 2 singletons, theme and parsers, the first is used to handle and provide the theme and the second one to parse it and generate styles. Even if it's not hard to understand this concept, most of the times the only methods we need to use of these 2 singleton are:

  • theme.get to get the current theme
  • theme.set to update the theme
  • parsers.resolve to parse the style object with the theme and generate valid css-in-js objects.

Solution

My idea is to create another singleton morfeo that exposes only 5 methods:

  • add to add a new theme with a given name (for example "light" or "dark")
  • use to use one of the previously added themes
  • set same as theme.set
  • get same as theme.get
  • resolve same as parsers.resolve
    With this solution, the new singleton morfeowill have an easy way to handle multi-theming and, most importantly, will expose only the API that 99% of the time we need; theme and parsers singletons will still be available for advanced usage for example add new parsers or easily get single slices or values from the theme.

Example

import { morfeo } from "@morfeo/core";
import { lightTheme, darkTheme } from "./themes";
 
morfeo.add("light", lightTheme);
morfeo.add("dark", darkTheme);

morfeo.use("light");

const style = morfeo.resolve({
    py: "m",
    gradient: "primary"
});

// will be the current theme, `lightTheme` in this case
const currentTheme = morfeo.get();
// optionally you can pass the name of the theme you want to get
const otherTheme = morfeo.get('dark');

Alternatives

Another alternative could be merge parsers and theme singletons completely, but I think the interface of the resulted singleton will expose too many API and it will be hard to use properly.

Add benchmarks to website

Current

We currently have a script that tests the performance of Morfeo packages and compares the core-api with other popular libraries, currently we have tests for:

  • core package
  • jss package
  • Morfeo core versus styled system

The results of these tests are currently available in the subfolder results inside apps/benchmarks as markdown files.

Enhancement

My idea is to update the scripts and instead of writing the results in the current folder, we can write them inside the documentation website, in this way we make performance tests available through the website morfeo.dev.

Future improvements

This new scripts could be run every time there is a new version of Morfeo so benchmarks will always be aligned to new functionalities and updates.

Post version script

The problem

Some Morfeo's packages assumes that you already have have other packages installed, for example @morfeo/styled-components-web assume that you already have @morfeo/react in your dependencies; This behaviour it's simply implement by putting (in the previous example) @morfeo/react as peerDependency of @morfeo/styled-components-web.

Unfortunately, lerna does not handle peerDependencies pretty good, in fact for every package that has another Morfeo package as peerDependency we have to:

  • During development: Always add the dependency not only in peer but also inside the devDependencies
  • During versioning: Manually update peerDependencies according to the new version.

This issue is caused by the process of sym-linking of lerna that doesn't care about peerDependencies.

Solution

My idea it's to create a simple script to run post-versioning where we syncronized the new version of the morfeo's packages in all the peerDependecies.
To do this we can simply read what the new version is from the lerna.json file and wherever we find Morfeo's peerDependency inside any package.json, we put this new version: ^${versionReadFromLernaJson}.

Additional

All the custom scripts we currently have are written in .mjs files, it could be good to convert them in Typescript and use ts-node to run them, I think it could be good to integrate this activity in this task.

Spec Improvements

This is a proposal to improve the default slice interfaces in order to have a more scalable system and a more intuitive theme building.

Sizes

The xxs and xxl could become 2xs and 2xl in order to suggest a more scalable naming for all sizes props.

Font Slice

As the font slice keys could be used to define different fonts and even the same fonts with different weights and styles it could be better to leave this slice more flexible by changing regular, medium, bold to just one default property default.

Borders

primary and secondary keys seem to not work properly to define borders. We could change them with some more appropriate like thin and `bold.

Zindices

light, soft and strong keys seem to not work properly to define zIndices. We could change them with some more appropriate like `lowest, lower, low, high, higher, highest.

@morfeo/cli package

Creation of the CLI for morfeo, this CLI will have a set of functionalities to easily use the morfeo eco-system.
In the first version I'm planning to implement 2 main functionalities:

  • generate static SCSS from the morfeo theme
  • merge design files in the main theme.

Generation of static style files

My idea is to use tools like style dictonary to build static style files for any platform (web, android, ios, flutter), in this way morfeo can be used inside other platforms other than JS-Oriented environments. Another reason is, since we can have static stylesheets based on the theme we can drastically increase performance but still ensure UI consinstency and a single source of truth for the styles

โœจ feat: add a states property for components

๐Ÿค” Problem

Right now, morfeo doesn't allow inheriting the variant style to create a different state of a component based on it. It forces us to make multiple variants with almost the same style and some little differences.

๐Ÿš‘ Solution

It would be great to have a states object in the component definition to override the variant style only for specific properties:

{
  ...theme,
  components: {
    Button: {
       ...componentConfig,
       states: {
          success: {
             bg: 'success'
          }
       }
       variants: {
         ...variants,
         outlined: {
            ...variant,
            states: {
               success: {
                 borderWidth: 's',
                 borderColor: 'success'
               }
            }
         }
       }
    }
  }
}

So, we can define a component in this way:

import { useStyle } from '@morfeo/react';

function Button({ children }) {
  const style = useStyle({ componentName: 'Button', variant: 'outlined', state: 'success' });
  return <button style={style}>{children}</button>;
}

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • chore(deps): update codecov/codecov-action action to v3.1.6
  • chore(deps): update dependency @crxjs/vite-plugin to v2.0.0-beta.23
  • chore(deps): update dependency @types/babel__core to v7.20.5
  • chore(deps): update dependency @types/babel__traverse to v7.20.5
  • chore(deps): update dependency @types/chrome to v0.0.267
  • chore(deps): update dependency @types/jest to v29.5.12
  • chore(deps): update dependency @types/prismjs to v1.26.3
  • chore(deps): update dependency @types/react-transition-group to v4.4.10
  • chore(deps): update dependency csstype to v3.1.3
  • chore(deps): update dependency eslint-plugin-import to v2.29.1
  • chore(deps): update dependency ts-jest to v29.1.2
  • chore(deps): update dependency ts-node to v10.9.2
  • chore(deps): update testing-library monorepo (@testing-library/dom, @testing-library/jest-dom, @testing-library/react)
  • fix(deps): update dependency @inquirer/input to v1.2.16
  • fix(deps): update dependency glob to v10.3.12
  • fix(deps): update nextra monorepo to v2.13.4 (nextra, nextra-theme-docs)
  • chore(deps): update dependency @types/node to v18.19.31
  • chore(deps): update dependency @vitejs/plugin-react to v4.2.1
  • chore(deps): update dependency eslint to v8.57.0
  • chore(deps): update dependency eslint-plugin-jsx-a11y to v6.8.0
  • chore(deps): update dependency eslint-plugin-react to v7.34.1
  • chore(deps): update dependency prettier to v3.2.5
  • chore(deps): update dependency react-native to v0.74.0 (react-native, @types/react-native)
  • chore(deps): update dependency turbo to v1.13.3
  • chore(deps): update dependency typescript to v5.4.5
  • chore(deps): update typescript-eslint monorepo to v6.21.0 (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • fix(deps): update babel monorepo (@babel/core, @babel/plugin-syntax-jsx, @babel/preset-env, @babel/preset-typescript, @babel/traverse, @babel/types)
  • fix(deps): update dependency @changesets/cli to v2.27.1
  • fix(deps): update dependency @codesandbox/sandpack-react to v2.13.10
  • fix(deps): update dependency @vercel/og to v0.6.2
  • fix(deps): update dependency chokidar to v3.6.0
  • fix(deps): update dependency clsx to v2.1.1
  • fix(deps): update dependency framer-motion to v10.18.0
  • fix(deps): update dependency next to v14.2.3
  • fix(deps): update dependency polished to v4.3.1
  • fix(deps): update dependency react-intersection-observer to v9.10.1
  • fix(deps): update dependency sharp to v0.33.3
  • fix(deps): update dependency webextension-polyfill to v0.11.0 (webextension-polyfill, @types/webextension-polyfill)
  • chore(deps): update codecov/codecov-action action to v4
  • chore(deps): update dependency @types/node to v20
  • chore(deps): update dependency chrome-webstore-upload to v3
  • chore(deps): update dependency eslint to v9
  • chore(deps): update dependency eslint-config-next to v14
  • chore(deps): update dependency tsup to v8
  • chore(deps): update pnpm to v9
  • chore(deps): update pnpm/action-setup action to v3
  • chore(deps): update testing-library monorepo (major) (@testing-library/dom, @testing-library/react)
  • chore(deps): update typescript-eslint monorepo to v7 (major) (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • fix(deps): update dependency @inquirer/input to v2
  • fix(deps): update dependency commander to v12
  • fix(deps): update dependency cosmiconfig to v9
  • fix(deps): update dependency framer-motion to v11
  • ๐Ÿ” Create all rate-limited PRs at once ๐Ÿ”

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/actions/setup-environment/action.yml
  • pnpm/action-setup v2.4.0
  • actions/setup-node v4
.github/workflows/build-web-extension.yml
  • actions/checkout v4
.github/workflows/codecov.yml
  • actions/checkout v4
  • codecov/codecov-action v3.1.4
.github/workflows/deploy-web-extension.yml
  • actions/checkout v4
.github/workflows/publish.yml
  • actions/checkout v4
  • changesets/action v1
npm
docs/package.json
  • @codesandbox/sandpack-react 2.9.0
  • @codesandbox/sandpack-themes 2.0.21
  • @lottiefiles/react-lottie-player 3.5.3
  • @vercel/og 0.5.20
  • framer-motion 10.16.4
  • next 14.0.0
  • nextra 2.13.2
  • nextra-theme-docs 2.13.2
  • react-intersection-observer 9.5.2
  • sharp 0.32.6
  • eslint-config-next 13.5.6
  • react 18.2.0
  • react-dom 18.2.0
package.json
  • @changesets/cli 2.26.2
  • csstype 3.1.2
  • react 18.2.0
  • react-dom 18.2.0
  • react-native 0.72.6
  • @testing-library/dom 9.3.3
  • @testing-library/jest-dom 6.1.4
  • @testing-library/react 14.0.0
  • @types/glob 8.1.0
  • @types/jest 29.5.6
  • @types/node 18.18.7
  • @types/react 18.2.31
  • @types/react-dom 18.2.14
  • @typescript-eslint/eslint-plugin 6.9.0
  • @typescript-eslint/parser 6.9.0
  • concurrently 8.2.2
  • eslint 8.52.0
  • eslint-plugin-import 2.29.0
  • eslint-plugin-jsx-a11y 6.7.1
  • eslint-plugin-react 7.33.2
  • eslint-plugin-react-hooks 4.6.0
  • jest 29.7.0
  • jest-environment-jsdom 29.7.0
  • prettier 3.0.3
  • ts-jest 29.1.1
  • ts-node 10.9.1
  • tslib 2.6.2
  • tsup 7.2.0
  • turbo 1.10.16
  • typescript 5.2.2
  • pnpm 8.9.2
packages/babel-plugin/package.json
  • @babel/traverse 7.23.2
  • @babel/types 7.23.0
  • @babel/preset-env 7.23.2
  • @babel/plugin-syntax-jsx 7.22.5
  • @babel/preset-typescript 7.23.2
  • @babel/core 7.23.2
  • @types/babel__core 7.20.3
  • @types/babel__traverse 7.20.3
  • @babel/core 7.23.2
packages/compiler/package.json
  • @babel/core 7.23.2
  • @inquirer/input 1.2.13
  • chokidar 3.5.3
  • commander 11.1.0
  • cosmiconfig 8.3.6
  • cosmiconfig-typescript-loader 5.0.0
  • glob 10.3.10
  • @types/babel__core 7.20.3
packages/core/package.json
packages/dev-tools/package.json
packages/hooks/package.json
  • react 18.2.0
  • react-dom 18.2.0
packages/jss/package.json
  • jss 10.10.0
  • jss-preset-default 10.10.0
packages/native/package.json
  • @types/react-native 0.72.5
  • react 18.2.0
  • react-native 0.72.6
packages/preset-default/package.json
packages/react/package.json
  • csstype 3.1.2
  • react 18.2.0
  • react-dom 18.2.0
  • @testing-library/jest-dom 6.1.4
  • @testing-library/react 14.0.0
packages/spec/package.json
packages/utils/package.json
packages/web/package.json
  • csstype 3.1.2
web-extension/package.json
  • change-case 4.1.2
  • clsx 2.0.0
  • polished 4.2.2
  • prismjs 1.29.0
  • react 18.2.0
  • react-dom 18.2.0
  • react-transition-group 4.4.5
  • webextension-polyfill 0.10.0
  • @crxjs/vite-plugin 2.0.0-beta.19
  • @types/chrome 0.0.248
  • @types/prismjs 1.26.2
  • @types/react-transition-group 4.4.8
  • @types/webextension-polyfill 0.10.5
  • @vitejs/plugin-react 4.1.0
  • chrome-webstore-upload 1.0.0
  • vite 4.5.0
  • zip-folder 1.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

Use `useSyncExternalStore` to sync morfeo's theme with react

Morfeo is born to work everywhere, it already works with React, Svelte, Angular or any other JS framework. Anyway for any of these framework we usually write packages to make the integration as good as possible, for example with directives for svelte or hook for react.

React v18 introduced a new hook called useSyncExternalStore, this hook helps to sync external states with the react state.
It seems to be the perfect solution to always be sure that the theme used (and returned) by the hooks or @morfeo/hooks is the current used inside the morfeo instance.

It will probably unlock even other functionalities like:

  • smart re-render (only if values used by the component changes and not every time the theme changes)
  • cuncurrent mode compatibility (not sure it's not working right now)

References:

Before proceeding with this issue, we need to upgrade to React 18.
The upgrade to React 18 is covered by this issue

Font slices crash if there are no fonts in the theme

Describe the bug
If there are no fonts in the fonts slice, the other font slices crash.

To Reproduce
Steps to reproduce the behaviour:

  1. Open the devtool while navigating the morfeo docs
  2. Inside the docs, go to the section Single source of truth
  3. Click on Apply Theme 1 to add 3 new themes to the morfeo theme
  4. Inside the devtool go to the letter spacings slice
  5. The devtool crash

Expected behaviour
The devtool should not crash and the slices should show a default font

Additional context
devtool version 0.1.0

Change of morfeo.useTheme method name

The problem

I think that the useTheme method of morfeo can result ambiguous in a react environment, for example:

const { useTheme } = morfeo;
// Can be confused with the hook `useTheme` of `@morfeo/hook`
useTheme('dark');

Also other problems can occur, for example:

const { useTheme } = morfeo;
if (condition) {
  // this is completely legal, since `useTheme` is not a hook (in this case) so we are not calling
  // an hook conditionally. 
  // but the linter will complain since it will not understand that useTheme is not a hook.
  useTheme('dark');
}

Solution

Instead of useTheme we can rename this method setCurrent, for me it makes sense even because we have a similar method called getCurrent that retrieves the name of the current theme:

const { setCurrent } = morfeo;
// No ambiguity with `useTheme`
setCurrent('dark');

Also. since all the other methods related to the theme contains the suffix theme, we can this about rename not only useTheme but also getCurrent in this way:

  • morfeo.useTheme will become morfeo.setCurrentTheme
  • morfeo.getCurrent will become morfeo.getCurrentTheme

โœจ feat: Build time

We think that having a strong theming system is the only way for applications to have consistent and beautiful UIs - And Morfeo is, in our opinion, one of the best choices if we talk about theming systems.

But these kinds of tools sometimes have a price to pay: performance.

๐Ÿค” Problem

Morfeo runs at run time, which means that every time you're using morfeo to create a style for your components, your device is executing some code in order to transform something like this: { componentName: 'Button' } into some complex css and then append this CSS into the DOM. Most of the time you'll not even notice, because we try to be as fast as we can doing it, but even with all the optimization we can provide, Morfeo will never be as fast as just simple and plain static CSS.

Not only this, there is nothing that Morfeo can do and regular static CSS cannot - so why you should use Morfeo in the first place?
The answer is pretty simple: Constraints!

Morfeo exposes a strongly typed and developer-friendly interface that ensures that every generated style is following the design language; In other words, Morfeo was built to make you do the right thing in the simplest possible way and the wrong thing in the hardest possible way.

CSS is, unfortunately, not like this. In CSS there is no difference (in terms of complexity) between doing the right thing and the wrong thing (like declaring variables and using those variables instead of random sizes/colors in every style).

Actually, I'd say that doing the right thing could be most of the time even a little bit harder, but let's say this is just speculation.

As of now, we had to make a choice between the control the tools like Morfeo can give and the performance that simple CSS has.

So the question is Slower and Consistent or Fast and Chaotic? After a lot of thought, I have to be transparent and say that Fast and Chaotic was my answer (basically, I'm going against the tool I've made) because I can work on the "chaos" and make even plain CSS organized and scalable. It's hard but not impossible.

But let's be honest... the goal is to be Fast And Consistent - the performance of plain CSS and the interface of Morfeo would be awesome in my opinion - is there a way to do it? YES, we think we found it and I can't wait to show you!

Let's look at this snippet here:

const style = morfeo.resolve({
   bg: 'primary',
   px: 'm'
});

I can easily predict how this will be resolved:

const style = {
   backgroundColor: '<the value of the primary color>',
   paddingLeft: '<the value of the size m>',
   paddingRight: '<the value of the size m>',
}

And so even the equivalent CSS

.className {
  background-color: var(--colors-primary);
  padding-left: var(--spacings-m);
  padding-right: var(--spacings-m);
}

Do you get the point? There is no need for a runtime, we can always do the job at build time! We just need a transpiler!

๐Ÿš‘ Solution

The idea is to expose a method called parse from the instance of morfeo that will never executed at runtime:

// morfeo.parse exposes the same interface of the function `getStyles` from `@morfeo/web`
const getClasses = morfeo.parse({
   button: {
     componentName: 'Button',
     variant: 'primary'
   },
})

const Button = () => {
  const classes = getClasses();
  return <button className={classes.button} />
}

If you try to execute morfeo.parse at runtime, an Expection will be thrown:

Error: morfeo.parse is not available at runtime, be sure you transpiled your code.

This method, in fact, does not to anything, is just a placeholder for a custom babel plugin. The object inside the function will be extracted, and resolved at build time, and the resulting code in your build will become something like this:

const getClasses = () => ({
   button: 'component-name-button variant-primary'
})

const Button = () => {
  const classes = getClasses();
  return <button className={classes.button} />
}

Let's look at the diff:

-const getClasses = morfeo.parse({
-   button: {
-     componentName: 'Button',
-     variant: 'primary'
-   },
-})
+const getClasses = () => ({
+   button: 'component-name-Button variant-primary'
+})

const Button = () => {
  const classes = getClasses();
  return <button className={classes.button} />
}

The plugin will also create and include in your build real CSS that will look something like this:

.component-name-Button.variant-primary {
   /* the generated css */
}

As you can see, by doing like this we can guarantee both the advantages: the consistency that a tool like Morfeo can give and the performance of simple CSS.

๐Ÿ“ Additional context

There are multiple other scenarios that we have to consider in order to provide the most versatile solution, for example the possibility to generate dynamic style based on some properties passed to morfeo.parse at runtime, for example:

// morfeo.parse exposes the same interface of the function `getStyles` from `@morfeo/web`
const getClasses = morfeo.parse({
   button: {
     componentName: 'Button',
     variant: props => props.variant
   },
})

const Button = ({ variant }) => {
  const classes = getClasses({ variant });
  return <button className={classes.button} />
}

In this case, the resulting code will (probably) become:

const getClasses = (props) => ({
   button: ((props) => `component-name-button variant-${props.variant}`)(props))
})

const Button = ({ variant }) => {
  const classes = getClasses({ variant });
  return <button className={classes.button} />
}

And the generated CSS has to provide all the possible styles instead of the specific one:

.component-name-Button.variant-primary {
   /* the generated css */
}

.component-name-Button.variant-secondary {
   /* the generated css */
}

/* all the possible variants of the button */

.component-name-Button.variant-round {
   /* the generated css */
}

But we are still working and thinking about what the best solution could be.

add documentation for each theme slice

Inside the page theme specification there is an overview of all the theme slices, but we are still missing a detail page
for each slice where to find infos like:

  • the slice interface
  • a description
  • use cases
  • css properties related to that slice

โœจ feat: Support Animation

๐Ÿค” Problem

Currently, Morfeo does not give any additional utilities regarding animations, the property animation is accepted by the resolve method but will be treated as a regular CSS property. Also, there is no way to generate @keyframes.

๐Ÿš‘ Solution

Prosal #1: animations theme's slice

Animations, just like colors, shadows, spacings, and so on... are part of the Design Language of any application, so they should be defined inside the theme.

To do it, we will need a new theme slice called animations, I think that a good way to do it could be the following:

type KeyFrameKey = `${number}%` | 'from' | 'to';

type KeyFrames = {
  [KeyFrameKey]: Style,
};

type Animation = Record<string, {
  delay?: number;
  fillMode?: 'none' | 'forwards' | 'backwards' | 'both' | 'initial' | 'inherit';
  duration?: number;
  direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse' | 'initial' | 'inherit';
  keyframes: KeyFrames;
  timingFunction?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'step-start' | 'step-end' | `steps(${number}, ${'start' | 'end'})` | `steps(${number})` | `cubic-bezier(${number}, ${number}, ${number}, ${number})` | 'initial' | 'inherit';
  iterationCount?: number;
}>;

Then, of course, we will need a parser to parse the property animation based on theme configuration.
This parser, and also the definition of the slice, should be added to the package @morfeo/web.

Usage

Once defined, they can be probably easily used in the following way:

const classes = morfeo.css({
   container: {
      '&[aria-busy="true"]': {
           animation: 'loading'
       }
   }
})

Prosal #2: animations and keyframes as theme's slices

Similar to the first one but:
Instead of putting the keyframes inside the Animation type, we can create another slice called keyframes instead:

type KeyFrameKey = `${number}%` | 'from' | 'to';

- type KeyFrames = {
+ type KeyFrameConfig = {
  [KeyFrameKey]: Style,
};

+ type KeyFrames = Record<string, KeyFrameConfig>;
+ type KeyFrame = keyof KeyFrames;

type Animation = Record<string, {
  delay?: number;
  fillMode?: 'none' | 'forwards' | 'backwards' | 'both' | 'initial' | 'inherit';
  duration?: number;
  direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse' | 'initial' | 'inherit';
-  keyframes: KeyFrames;
+  keyframes: KeyFrame;
  timingFunction?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'step-start' | 'step-end' | `steps(${number}, ${'start' | 'end'})` | `steps(${number})` | `cubic-bezier(${number}, ${number}, ${number}, ${number})` | 'initial' | 'inherit';
  iterationCount?: number;
}>;

This way we can also reuse the same keyframe in multiple animations.

Prosal #3: no theme's slice, new API

We can even think to completely change the approach and create a new API like morfeo.keyframes:

morfeo.keyframes({
   loading: {
        '0%': {
          bg: 'gray.dark'
        },
       '100%': {
          bg: 'gray.light'
        }
     } 
   }
});

const classes = morfeo.css({
   loader: {
      animation: 'loading'
   }
})

Create a set of primitive components for web applications

Is your feature request related to a problem? Please describe.
Create a set of ready to use primitive components as a new package (@morfeo/primitives). At now the morfeo eco-system doesn't provide a way to quick start with the UI development. This feature should reduce the amount of boilerplate needed to start to develop with Morfeo.

Describe the solution you'd like
I'm not sure about what approach could be the best for this feature yet. A solution that can work fine on all frameworks could be to develop the set of primitives as web components by using a library like stenciljs (https://stenciljs.com/).

Describe alternatives you've considered
Another approach could be to develop a common set of component styles by using the theme specs of morfeo and then develop separated solutions for each of the main frameworks and libraries:

  • primitives-react
    -primitives-web-components
  • primitives-vue
  • primitives-svelte
  • primitives-angular

typings of responsive values for fallback properties

Describe the bug
Morfeo except values in 2 ways, by specifying an alias that reference the theme of by passing an object that scales to breakpoints.
The bug is related to those properties that comes from the type augmentation inside @morfeo/web, for those properties even if morfeo handle responsive values, the IDE is not suggesting the properties.

To Reproduce
Steps to reproduce the behavior:

  1. make a component
  2. try to pass to a property like flexDirection this value: { sm: 'row', md: 'column' }
  3. See error

Expected behavior
The IDE suggest the breakpoints and for each the possible values

Unify builds types

Most of morfeo's package are builded in both esm and commonjs module types.

Currently there are 2 separate build scripts in each of these packages:

  • build for esm
  • builld:commonjs for commonjs

During the bootstrap of lerna, we run the commands: lerna run build and lerna run build:commonjs to be sure we are aways creating the builld for each types.

Problems

Even tho is not a huge problem, what happen sometime is that you build a single package (for example by running the command lerna run build:commonjs --scope=@morfeo/{packageName}) is that the build forcommonjs is ready but the esm one still refers to the old version.

Solution

I think it's just better to unify this process in a single build script for each package instead of having 2, this build script will prepare the build for both types (in case is needed).

Example

The package can have instead of just build and build:commonjs, another script: build:esm, so we can edit the build script in this way:

{
    "build": "yarn build:esm && yarn build:commonjs",
    "build:esm": "rimraf build && tsc",
    "build:commonjs": "rimraf commonjs && tsc --module CommonJS --outdir commonjs",
}

MorfeoComponent

Extract from the web-sandbox the generic component MorfeoComponent and move it inside the package @morfeo/react.

The component can be used easily render components defined inside the theme.

Examples

// Your theme
const theme = {
  ....,
  components: {
      Button: { ... }
  }
};
return <MorfeoComponent componentNam="Button" />
return <MorfeoComponent componentNam="Button" variant="primary" />
return <MorfeoComponent componentNam="Button" style={{ bg: 'primary' }} />

useClassNames hook

Creation of a new hook: useClassNames to use in react

motivation

In a React environment, the current hooks morfeo has are exposed by @morfeo/hooks package, in particular wi the hooks:
useStyles and useStyle it's possible to generate style based on the theme, for example:

const styles = useStyles({
  container: {
    bg: 'background',
    px: {
      xs: 'm',
      lg: 'l',
    }
  },
  button: {
    '&:hover': {
       opacity: 'soft'
    },
  }
});

return (
  <div style={styles.container}>
     <button style={styles.button}>Example</button>
  </div>
)

There is a problem with useStyle(s):
inline style can not handle pseudo classes or media queries;
so in the previous example, the stye related to &:hover and px will not work as expected.

solution

To handle pseudo elements and media queries we need to generate css and give to components classNames instead of inline styles, for example:

const classNames = useClassNames({
  container: {
    bg: 'background',
    px: {
      xs: 'm',
      lg: 'l',
    }
  },
  button: {
    '&:hover': {
       opacity: 'soft'
    },
  }
});

return (
  <div className={classNames.container}>
     <button className={classNames.button}>Example</button>
  </div>
)

resources

The expected behavior of useClassNames is more less the same as useStyles from react-jss:

โœจ feat: Introduce a new hook to handle the current theme in React

๐Ÿค” Problem

In React we have hooks to access or get the theme object (eg .useTheme, or useProps) and to resolve the style (eg.useStyles), but we don't have anything related to the current theme name or a way to change it, in other words the equivalent of the core-api: morfeo.getCurrentTheme and morfeo.setCurrentTheme.

Of course we can still use the core-api to retrieve this value, but we always have to care by ourself about the subscription the theme changes and keep track of this value.

๐Ÿš‘ Solution

My proposal is to add an hook called useCurrentTheme.

The signature of this function would be:

export function useCurrentTheme(): [ThemeName, (name: ThemeName) => void]

And it works in this way:

function Component() {
  const [currentTheme, setCurrentTheme] = useCurrentTheme();

  return (
    <div>
      Current theme: {currentTheme}
      <button onClick={() => setCurrentTheme('dark')}>Toggle</button>
    </div>
  );
 }

Valid tag crashes @morfeo/styled-components-web

Describe the bug
If the specified tag is not related to a theme component the app crashes.

To Reproduce
Steps to reproduce the behavior:

  1. create a components using @morfeo/styled-component-web
  2. create the component using any HTML tag
  3. start the application
  4. See error

Expected behavior
The component is created with the specified tag

improve borders slice and remove borderStyles slice

Discussed in https://github.com/VLK-STUDIO/morfeo/discussions/61

Originally posted by mauroerta July 31, 2021
Currently borders slice is used to create aliases of borders we want to use in our application, for example:

{
 "colors": {
    "primary": "#F9F8F8",
    "secondary": "#CDD3CE",
  },
  "borderWidths": {
    "s": "3px",
    "m": "5px",
  },
  "borders": {
    "none": "none",
    "primary": "5px solid white",
    "secondary": "3px solid  black",
  }
}

The problem is that there is no way to reference the slice colors to use colors from the theme or the slice borderWidths to use mapped border widths. In the same way we cannot reference mapped border styles, but in this case the only possible values that we can set are the ones supported by border-style property.

I wanted to improve borders since I think currently it's a little bit useless. I'm opening this discussion to have some feedback about 2 proposals:

First proposal

Make the border value an object with width, color, and style:

{
  "colors": {
    "primary": "#F9F8F8",
    "secondary": "#CDD3CE",
  },
  "borderWidths": {
    "s": "3px",
    "m": "5px",
  },
  "borders": {
    "none": {},
    "primary":  { "width": "s", "color": "primary", "style": "solid" },
    "secondary": { "width": "s", "color": "secondary", "style": "dotter" },
  }
}

In this way the generated border will always reference acceptable values.

Second proposal

The second proposal is to remove borderStyles slice since we can only accept values described here so I don't think we need a dedicated theme slice

[CLI] cli command for design files

Merge design files in the main theme

I think it could be useful in terms of developer experience to write the style of the components close to the component it self, for example:

-- src
-- -- components
-- -- -- MyComponent
-- -- -- -- MyComponent.tsx
-- -- -- -- MyComponent.morfeo.ts
-- -- -- -- index.ts

The content of MyComponent.morfeo.ts can be something like:

export default {
  tag: 'button',
  style: {
    bg: "primary",
    borderRadius: "m",
    px: "l",
   // ...
  },
  variants: {
    // ...
  },
}

Then, by running something like:

morfeo build-theme

The CLI will find all the {componentName}.morfeo.ts files and merge in the main theme:

// theme.ts
export default {
  colors: {....},
  spacings: {....},
  // ...
  components: {
    [componentName]: {
       // ... content from {componentName}.morfeo.ts
    }
  },
}

Predictable css class generator

When used in frameworks like NextJS, style generated at build-time or request-time in the server will produce css classes that they will be different from the one generated at run-time in the client; This inconsistency will cause unexpected behaviours like un-styled elements all around the application.

Scenario

Let's make an example:

function Button() {
  const className = useClassName({ componentName: 'Button', variant: 'primary' });

  return <Button className={className} />;
}

In the current implementation of Morfeo, we don't know what className will be, but let's say that the first time the page is generated at request-time will be button-0-0-1, This will also produce a some real Css that will be appended in the head of document, like:

.button-0-0-1 {
   background-color: #fff;
   color: #000;
   border-radius: 8px;
}

Once the page will be served to the client and rehydrated from Next, since the JSS context is different, a new className will be generated, let's say it will be button-0-1-2.
Since "button-0-0-1" !== "button-0-1-2" the css generated at request time will not be applied to the component.

Solution

The issue can be solved in different ways, for example frameworks/libraries like material-ui or styled-component clean css generated at request-time and force the app to re-generate CSS in client side.
I think tho that the real problem is just how we are generating class names, in fact, if the class name was the same in the server and in the client, the generated css would be applied to the component.

Technically, there is a way to always produce predictable class names, we can construct the class name from the css properties and values of our style, for example:

const style = {
    componentName: 'Button',
    variant: 'primary'
}

we can generate for this style a string like component-name-Button-variant-primary, that is just a concatenation of all the {propertyName}-{propertyValue} of the style itself.

corner alias for borderRadius property

add a new alias for the borderRadius property called corner:

const style = parsers.resolve({corner: 'm'}); // --> { borderRadius: '10px' }

How to do it

Other then corner it could be useful to define other radius-related props for like:

  • corner
  • cornerEndEnd
  • cornerTopLeft
  • cornerEndStart
  • cornerTopRight
  • cornerStartEnd
  • cornerStartStart
  • cornerBottomLeft
  • cornerBottomRight

those properties should be added in the radiiMap array inside the file packages/spec/src/properties/radii.ts:

export const radiiMap = [
  'borderRadius',
  'borderEndEndRadius',
  'borderTopLeftRadius',
  'borderEndStartRadius',
  'borderTopRightRadius',
  'borderStartEndRadius',
  'borderStartStartRadius',
  'borderBottomLeftRadius',
  'borderBottomRightRadius',
  // New aliases
  'corner',
  'cornerEndEnd',
  'cornerTopLeft',
  'cornerEndStart',
  'cornerTopRight',
  'cornerStartEnd',
  'cornerStartStart',
  'cornerBottomLeft',
  'cornerBottomRight',
] as const;

Then inside the parsers in @morfeo/core should be added a new parser to handle these new properties.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.