willofindie / vscode-cssvar

VSCode extension to support CSS Variables Intellisense
https://marketplace.visualstudio.com/items?itemName=phoenisx.cssvar
MIT License
252 stars 4 forks source link

[Feat] Support CSS variables defined by TypeScript #98

Closed penx closed 1 year ago

penx commented 2 years ago

Is your feature request related to a problem? Please describe.

When using Chakra UI, and calling the extendTheme function:

import { extendTheme } from "@chakra-ui/react"

const colors = {
  admin: {
    dark: '#000',
  }
} as const

const theme = extendTheme({
  colors
});

CSS variables will be made available of the format --chakra-colors-admin-dark.

Describe the solution you'd like

If I could define a TypeScript type as follows:

type ColorVars = "--chakra-colors-admin-dark" | "--chakra-colors-admin-light" | "--chakra-colors-admin-rebeccapurple"

It would be great to be able to point vscode-cssvar at this type and have it pick up these variables to include them in autocomplete.

Describe alternatives you've considered

Somehow generate a css file from the typescript type or call to extendTheme.

Additional context

I may be able to help contribute this feature if you give me some pointers.

phoenisx commented 2 years ago

I think this: https://github.com/willofindie/vscode-cssvar/issues/92 is what you are trying to solve by creating a TS type.

JSON data is more informative compared to TS types and is widely known & used. I feel it would be good to use .json to define dynamically generated CSS custom props (If that's what you are looking for).😉

If you want to contribute, you can check src/main.ts#setup and src/parser.ts#parseFile methods, where we need the changes, and conditionally handle parsing a CSS or JSON file. 👍🏽

penx commented 2 years ago

Thanks!

Loading from a JSON file means we would need a build step to create a JSON file when the colors object changes to keep up to date, and ideally to have something running in the background to build when there are changes.

Loading from the TypeScript type means the CSS variables would come directly from the source code, not requiring a background task or build step.

I would prefer the TypeScript support, but yes I think #92 is solving the same problem.

Thanks for creating this library <3

penx commented 2 years ago

In case it helps illustrate what I mean, the type would come straight from the source code by using:

const colors = {
  dark: '#111',
  light: '#EEE',
  admin: {
    dark: '#000',
    light: '#FFF',
    rebeccapurple: '#639'
  }
} as const

type Squashed<T> = keyof {
  [Property in keyof T as T[Property] extends string | number | boolean | null
    ? Property
    : `${string & Property}-${string & Squashed<T[Property]>}`]: true;
};

type ColorVars = `--chakra-colors-${Squashed<typeof colors>}`
// "--chakra-colors-dark" | "--chakra-colors-light" | "--chakra-colors-admin-dark" | "--chakra-colors-admin-light" | "--chakra-colors-admin-rebeccapurple"

Playground

penx commented 2 years ago

I was thinking that, given a a path to file in the workspace, a vscode extension could connect to the TS language server and load the types for that file.

I thought there was something in the vscode API to do this, but I can't find it, nor can I find any documentation to help.

Please let me know if you've seen anything :)

phoenisx commented 2 years ago

If you want typing support, isn't Typescript already providing that for the dynamic CSS variables created by Chakra?

Can u help me understand what you are looking for with an example?

penx commented 2 years ago

Say we're defining our CSS variables as follows:

import { extendTheme } from "@chakra-ui/react";

const themeExtension = {
  colors: {
    dark: '#111',
    light: '#EEE',
  }
} as const

export const theme = extendTheme(themeExtension)

And then using them as:

const someCss = css({
  backgroundColor: "var(--chakra-colors-dark)",
})

...it would be good to have code hinting in vscode for the variable names, generated from the TypeScript type.

No, Chakra doesn't provide any typing to help with this as far as I'm aware.

I think you're right, in our use case we can enforce this with TypeScript.

If we were defining CSS variables in TypeScript and then using them in CSS files, this wouldn't work - but we're not doing that :)

phoenisx commented 2 years ago

@penx I think what you are talking about is not Typing support for the CSS variable, but instead, IntelliSense (auto-completion) support, that this extension provides.

This extension requires at least a key-value pair ([key]: CSS variable name, and [value]: CSS variables value) to provide CSS variable IntelliSense. This was the reason I mentioned here: https://github.com/willofindie/vscode-cssvar/issues/98#issuecomment-1307438203, to use JSON, as it will contain required metadata, which Typescript types won't be able to provide.

penx commented 2 years ago

I think what you are talking about is not Typing support for the CSS variable, but instead, IntelliSense (auto-completion) support, that this extension provides

Originally yes, but I have since realised I can create a typed helper cssvar["colors-dark"] that autocompletes and is checked by TS.

This extension requires at least a key-value pair ([key]: CSS variable name, and [value]: CSS variables value

My thinking was that the value wouldn't be known, and the extension would set this to null/empty string for TypeScript defined CSS variables. But yes, a JSON file containing your themes and their respective values would be a better experience.

Feel free to close this issue! There may be a benefit to people defining their CSS variables in TypeScript and consuming them in CSS files, but that doesn't apply to us :)

penx commented 2 years ago

Our ts-only solution is as follows, in case anyone ends up here looking for something similar :)

import type { Theme } from "@chakra-ui/theme";
import { extendTheme } from "@chakra-ui/react";

const customTheme = {
  colors: {
    dark: '#111',
    light: '#EEE',
  }
} as const;

export const theme = extendTheme(customTheme);

export type CustomTheme = typeof themeExtension & Theme;

type ThemeObject = {
  [key: string]: number | string | ThemeObject;
};

type Squashed<T extends ThemeObject> = keyof {
  [Property in keyof T as T[Property] extends ThemeObject
    ? `${(string | number) & Property}-${(string | number) &
        Squashed<T[Property]>}`
    : Property]: true;
};

const tokens = [
  "colors",
  // "borders",
  // "fonts", etc.
] as const;

type Tokens = Squashed<Pick<CustomTheme, typeof tokens[number]>>;

export const cssvar = (key: Tokens) => {
  return `var(--chakra-${key})`;
};

Usage:

const MyComponent = styled("div")({
  color: cssvar("colors-dark"),
});
phoenisx commented 1 year ago

@penx Thanks for the detailed explanation.

I will close this issue and track support for dynamic variables in #92 Do comment on that issue, if you feel we can have a better approach!!