-
Notifications
You must be signed in to change notification settings - Fork 673
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
(css): Allow injecting UserTheme and typecheck colors against scale #1090
Conversation
A move towards "TypeScript: Type check property values against theme scales". As seen in `styled-components`, but we'll write tests for this 😉 https://styled-components.com/docs/api#create-a-declarations-file https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/styled-components/index.d.ts#L412
Ohh love this. If I understand correctly, I think it would close this dripsy issue? I've been thinking about the right way to do typescript intellisense a lot. This PR seems really elegant, I would definitely implement it. |
Oh! I was looking for that gif! You previously had it in the readme, am I right? I think merging this (okay, finishing and merging, there's still some work ahead) closes the issue in Dripsy and the only thing left to do there would be an update of Theme UI version. https://github.com/nandorojo/dripsy/blob/master/src/css/types.ts#L1-6 We don't need any codegen, the user will just opt-in to strict mode (forced design tokens mode?) with declaration merging. |
Yup I put it in the readme when I was first dreaming up dripsy and didn't want to forget to work on it. I can't wait to add it back on as a feature, not an idea. Super excited. Much better to avoid code gen. |
Okay, I've noticed a limitation. Without codegen we can't concatenate strings on type level. What does it mean? We can't statically know that {
colors: {
gray: ["#eee", "#bbb", "#333", "#111"]
}
} results in
so nested scales would need codegen. We could probably do a hack like type Color = keyof UserTheme['color'] | (string & {}) to allow arbitrary values. |
Does the new TypeScript 4.x tuple approach solve that? https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/ (I don't know the answer.) I typically use flat scales anyway, so I'd also be fine with that. I would prefer type consistency than nesting values. |
@hasparus Hey, just checking to see if you're still planning on working on this. Really excited for it. Relevant from Material UI: https://material-ui.com/components/about-the-lab/#typescript |
I'm wondering if we should migrate to It uses
Well, I am, but slowly :( Would you like to take over? |
Just to make tooltips and error messages smaller.
@hasparus No worries, I understand. I'm not sure I'm enough of a TS wizard for this one, but I'm happy to try to help...could you describe the steps for adding other scales? |
@cmaycumber any shot you've done this kind of declaration merging before? I'm going to work off this branch to try to solve nandorojo/dripsy#6, but I haven't done it before. (Not sure if this feature would be useful to your apps or not, but I think it's one of the more exciting things theme-ui/dripsy could add.) @hasparus would this PR get merged if we made progress on it? |
@nandorojo No, I have not. But this feature is super exciting and I'd love to see it in dripsy and theme-ui, I think it would be a great addition and would be definitely relevant in my apps/websites. On that note, I'd definitely help as much as possible. I'll try to create a fork of this branch and work with it a bit to see if I can help. |
Awesome, that's great to hear. I'm going to look into @hasparus work on this so far and try to do the same! |
@hasparus is there a repository you use somewhere that has the JSDoc comments for the CSS properties? For example, where did you get this from: export interface ColorScaleCSSProperties {
/**
* The **`color`** CSS property sets the foreground color value of an element's text and text decorations, and sets the `currentcolor` value. `currentcolor` may be used as an indirect value on _other_ properties and is the default for other color properties, such as `border-color`.
*
* **Syntax**: `<color>`
*
* **Initial value**: Varies from one browser to another
*
* | Chrome | Firefox | Safari | Edge | IE |
* | :----: | :-----: | :----: | :----: | :---: |
* | **1** | **1** | **1** | **12** | **3** |
*
* @see https://developer.mozilla.org/docs/Web/CSS/color
*/
color?: Color
...
} I don't see it in this format on mozilla's site. Thanks! |
OMG you guys are picking this up! I'm so grateful. Sorry for slow responses :c BTW good news: TypeScript 4.1 makes strictly typing nested scales possible.
All of these should already in Theme UI codebase. I think it's originally from CSSType, but there's no way to "inherit" these comments, so we need to copy them if we want them to appear in user tooltips. |
@hasparus no worries, thanks for the response. It might make sense for one of us to move this into our own PR if you don’t plan on working on it for now. Would you mind telling me if this is going in the right direction? hasparus#1 Thanks so much for this! Really excited for this feature. |
@nandorojo the Stitches PR I mentioned in Twitter DMs. Our code and tests will end up looking pretty similar I suppose. |
Ah, the GIF in that PR is so satisfying. Thanks for passing this along. |
I think that makes sense. btw, ts-toolbelt released a stable dot path notation getter for TS string templates that has higher performance. There’s more discussion towards the end of this issue: millsp/ts-toolbelt#154 I believe it’s |
54a44f0
to
410ac76
Compare
Okay, I've tested that canary release. It defiintely lacks polish, and I had to work around my circular references. I had to cast my export const makeStyles = (s: ThemeStyles) =>
s as Record<
string,
Record<string, number | false | string | undefined | null> | undefined
>; We could split our theme into "specification" and "implementation" parts — scales create define the theme, variants and styles use scales. So, theme: {
styles: {
root: {
color: theme => theme.styles.root.color
}
}
} Additional notes
|
When you say circular references, you're referring specifically to the function getters within the theme, right? (I don't actually use those myself, mostly just in component props, but never in the actual theme.) |
Yes. Functions in the theme get theme as the argument. (I'm wondering if TS would allow a circular reference if we made theme "methods" instead But functional styles aren't the only problem. Take a look at this: const root: ThemeUICSSObject = {
backgroundColor: "background"
}
const theme = makeTheme({
styles: { root }
})
type ExactTheme = typeof theme;
declare module "@theme-ui/css" {
interface UserTheme extends ExactTheme {}
}
CodeSandbox with the example: https://codesandbox.io/s/circular-explosions-4re43?file=/src/App.tsx We could change the API to work around this function makeScales<TExactScales extends Scales>(scales: TExactScales): TExactScales {
return scales
}
const scales = makeScales({ /* ... */ })
type MyScales = typeof scales
declare module "theme-ui" {
export interface UserScales extends MyScales {}
} And stop caring about keeping styles and variants in Theme type. |
@hasparus I think this makes sense. The declaration merging is only going to be once you create the app. It’s totally fine to ask people to have a Plus, if we have a |
Basically, I’m thinking the final theme type shouldn’t rely on the user theme. The theme has too many things that the user shouldn’t need to edit and will create circular references. They should just add their own types for colors, scales, etc, and the final theme object can pull all of those in. |
Also, since the “theme” itself doesn’t need intellisense, and only components do, maybe the user types should just get mapped onto the |
1e7ca29
to
67fa03d
Compare
In case it's of interest, I implemented this from scratch for Dripsy v3: nandorojo/dripsy#124 |
Any updates on this? Haven’t seen any commits in a while and was curious if it’s still a feature being actively developed. 🙏 |
@yerffejytnac I'm gonna salvage some parts of this PR, but after testing a prerelease we discovered this is not exactly the public API we'd like to have, as it can be a bit of a footgun. I'm recently using Theme UI a lot more than I used it in the past years, so while I won't promise you an expected release date, it's quite likely I'll need this or an alternative feature will get implemented soon :) |
Any updates on this? |
This is IMHO the coolest PR I've sent to this repo. An actual feature!
A move towards "TypeScript: Type check property values against theme scales" v1 goal. #832
I belive we can treat Theme UI as an embedded domain-specific language for branded styling.
Reasoning
Imagine you're working with strict designers. They specify what's on-brand and what's not, the list of colors, spacings etc.
We keep these design tokens in our theme scales, but we can still use arbitrary values, merge them to master, deploy and get a dissapointed slack message from a designer. What if we could constain ourselves to predefined values on type level to make sure we don't do this?
Now we can.
I kinda like the idea of the runtime working as it is now and supporting arbitrary values all the time.
They are useful for prototyping, even if we don't want to merge them to master.
If we decide to not build the app on type errors, we effectively make illegal states unrepresentable.
WIP
Any help is welcome! 🙌
Theme
depends on variants and scales. Variants depend on ThemeUIStyleObject. ThemeUIStyleObject depends onTheme
and "platform" CSS types.@nandorojo, will this work with dripsy? I'd guess so, but I only skimmed through your codebase.
The solution is similar to
DefaultTheme injection in
styled-components
:https://styled-components.com/docs/api#create-a-declarations-file
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/styled-components/index.d.ts#L412
And declaring config in Overmind:
https://overmindjs.org/core/typescript#1-declare-module.
Closes #1297.