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

Allow style component to be used as plugin #159

Closed
wants to merge 2 commits into from

Conversation

sastan
Copy link
Collaborator

@sastan sastan commented Mar 25, 2021

This PR allows a style component to be used as a plugin based on an idea by @mattrossman

Here are some example of possible class names (see open issues below for alternatives:

btn
btn-size=md-variant=primary
btn-variant=gray-size=sm
btn-outlined
btn-variant=gray-size=sm-outlined

Open Issues

  • format of serialized variants
    • Object like: btn-{size:md,outline}
    • ??? like: btn-{size=md,outline}
    • Kebab Case (tailwind like): x-size=md-camelCase (implemented in this PR)
    • Snake Case: x-size=md_camelCase & x-size=md_camel-case
    • BEM modifer: x-size=md--camelCase & x-size=md--camel-case
    • URL like: x-size=md&camelCase & x-size=bar&camel-case
  • enhance VS Code extension to recognize these as valid class names

Usage

setup({
  plugins: {
    btn: style({
      base: `rounded-full px-2.5`,

      variants: {
        // btn-size=[sm|md]
        size: {
          sm: `text-sm h-6`,
          md: `text-base h-9`,
        },

        // btn-variant=[gray|primary]
        variant: {
          gray: `
            bg-gray-400
            hover:bg-gray-500
          `,
          primary: `
            text-white bg-purple-400
            hover:bg-purple-500
          `,
        },
        // btn-outlined
        outlined: {
          true: `bg-transparent ring-1`,
        },
      },

      // Set defaults for properties
      defaults: {
        variant: 'gray',
        size: 'sm',
      },

      // Apply additional styling based on the combination of properties
      matches: [
        {
          // If props match { variant: 'gray', outlined: true } add ring-gray-400
          variant: 'gray',
          outlined: true,
          use: `ring-gray-400`,
        },
      ],
    }),
  },
})

@coveralls
Copy link

coveralls commented Mar 25, 2021

Pull Request Test Coverage Report for Build 687852949

  • 155 of 162 (95.68%) changed or added relevant lines in 6 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage decreased (-0.07%) to 95.821%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/twind/serialize.ts 41 48 85.42%
Files with Coverage Reduction New Missed Lines %
src/twind/serialize.ts 1 96.37%
Totals Coverage Status
Change from base Build 680710822: -0.07%
Covered Lines: 5236
Relevant Lines: 5382

💛 - Coveralls

@github-actions
Copy link
Contributor

size-limit report 📦

Path Size
twind 0 B (-100% 🔽)
twind/colors 0 B (-100% 🔽)
twind/css 0 B (-100% 🔽)
twind/observe 0 B (-100% 🔽)
twind/shim 0 B (-100% 🔽)
twind/style 0 B (-100% 🔽)
dist/twind.js 12.7 KB (+100% 🔺)
dist/css/css.js 1.14 KB (+100% 🔺)
dist/observe/observe.js 666 B (+100% 🔺)
dist/shim/shim.js 403 B (+100% 🔺)
dist/style/style.js 1007 B (+100% 🔺)

@github-actions
Copy link
Contributor

Try the Preview Package

Official releases are only available on registry.npmjs.org as twind.

This PR has been published to npm.pkg.github.com as @tw-in-js/twind@pr159.

Install/Update

Configure your NPM client (click to expand)
  1. Generate a personal access token with at least "read:packages" scope.
  2. Adjust your .npmrc to use the token and define the regeistry for @tw-in-js:
//npm.pkg.github.com/:_authToken=<READ_PACKAGES_TOKEN>
@tw-in-js:registry=https://npm.pkg.github.com

Using the command line:

npm config set //npm.pkg.github.com/:_authToken <READ_PACKAGES_TOKEN> --global
npm config set @tw-in-js:registry https://npm.pkg.github.com --global
# For npm
npm install --force twind@npm:@tw-in-js/twind@pr159

# For yarn - upgrade implies install
yarn upgrade twind@npm:@tw-in-js/twind@pr159

@mattrossman
Copy link

Cool :D thanks for working on this.

Regarding the serialization issue, I find the current and alternative approaches a bit difficult to read and grasp the structure. I can't immediately tell whether - or = is the higher priority separator. An idea to explore is keeping the "parameters" visually contained to help communicate that they work together to configure the component.

E.g. after experimenting with the plugins API further, I noticed a syntax like btn-[size=sm outlined] successfully passes through to parts, although this may cause confusion with the arbitary CSS brackets syntax. I also tried a syntax like btn-{size:sm outlined} to make it look similar to the JS object you'd normally pass to the component, but this gets ignored by the plugin system.

@danielweck
Copy link
Member

setup({
  btn: style({

Small correction (nit picking): the plugins: {} property is missing in the setup(...) function argument.

@danielweck
Copy link
Member

I like this proposal :)

The proposed classname syntaxes are not without their pitfalls. I imagine that there may be edge-cases such as variant names/values containing - characters, or other characters not supported by the CSS.escape() polyfill / NodeJS replacement (

export const escape =
(typeof CSS !== 'undefined' && CSS.escape) ||
// Simplified: escaping only special characters
// Needed for NodeJS and Edge <79 (https://caniuse.com/mdn-api_css_escape)
((className: string): string =>
className
// Simplifed escape testing only for chars that we know happen to be in tailwind directives
.replace(/[!"'`*+.,;:\\/<=>?@#$%&^|~()[\]{}]/g, '\\$&')
// If the character is the first character and is in the range [0-9] (2xl, ...)
// https://drafts.csswg.org/cssom/#escape-a-character-as-code-point
.replace(/^\d/, '\\3$& '))
). I too find the = key/value pairs hard to read, but I know from experience / experimentation that the current implementation of the plugin "parts" tokenizer (i.e. - -separated segments) is a bit "picky" (e.g. discards ( and ) characters, and I think curly braces too).

As some of you may know ( #147 ), my Preact WMR -based build system removes the Twind runtime in production builds (static SSR), which means that the Twind runtime exists in-browser only at development time, and otherwise in NodeJS during the prerender pass. In my current implementation I handle style() by pre-computing all declared variants. For example, hashed Twind classes like tw-fw0103 / tw-bo9mmw are generated ahead of time, exposed in the SSR'ed DOM (for hydration), with their corresponding CSS rules serialized in static stylesheets (either critical or deferred styles). JSX components that consume the tw-... Twind variant classes reference them indirectly via a symbolic name that is exposed by a Preact Context + Provider.

So, with this plugin-based style() proposal, I am hoping that I will be able to leverage Twind's built-in kebab/snake/etc. classname syntax directly (i.e. direct mapping to style() variants). I need to think about it more...

@sastan
Copy link
Collaborator Author

sastan commented Mar 26, 2021

About the syntax

We should keep in mind that people will demand autocompletion for that. So it should be "autocompletable". Maybe I just need to extend the typescript plugin to have special handling. In that case, it should be distinguishable from other values.

btn-{size:md,outline}
btn-{size=md,outline}

Between { and } the completions service could do a property autocompletion as we all know.

Edit: The parser would do the same as it does for arbitrary values (skip until closing bracket).

@sastan sastan force-pushed the main branch 6 times, most recently from 26239da to 137af16 Compare April 12, 2021 15:57
@sastan
Copy link
Collaborator Author

sastan commented Jan 29, 2022

Fixed in v1 with b728000#diff-365eceeb5bef7743d073424328e2ca80bd0cf695e77449e04b36aee2ce5e5043R30

@sastan sastan closed this Jan 29, 2022
@sastan sastan deleted the allow-style-component-to-be-used-as-plugin branch November 18, 2022 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants