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

v3 #124

Merged
merged 100 commits into from
Oct 26, 2021
Merged

v3 #124

merged 100 commits into from
Oct 26, 2021

Conversation

nandorojo
Copy link
Owner

@nandorojo nandorojo commented Sep 27, 2021

Code.-.App.tsx.dripsy.mp4

Closes #6, #72, #123 and many other requests for improved types & intellisense for Dripsy.

yarn add dripsy@canary

Features / Improvements 🥸

DX 👨🏽‍💻

  • Add intellisense for useDripsyTheme
  • Add intellisense for sx prop
  • Add intellisense for gradient and colors on @dripsy/gradient
  • Add intellisense for useResponsiveValue(theme => [theme.colors.background])
  • Add intellisense for useSx
  • Add intellisense for sx factory (sx={theme => ({ color: theme.colors.text })}
  • Add makeTheme function to override default one with a custom theme
  • Allow module declaration for merging TypeScript theme
  • Remove @theme-ui/core
  • Remove all Theme UI runtime code
  • Use @theme-ui/css for types only
  • Move DripsyProvider to use internal Dripsy context rather than Theme UI (BREAKING if you use the jsx pragma in your app)
  • Add intellisense for styled and createThemedComponent
  • Add intellisense for themeKey in styled
  • Add intellisense for variant and variants
  • Make intellisense for variant only pick based on the specified themeKey
  • Add scale-specific theme tokens. For instance, backgroundColor should only get tokens from the theme.colors. (Is this blocking? It could take a lot of effort. Not sure if it should be blocking)
  • Allow arrays in the theme (is this blocking?)
  • Map theme UI styles onto RN styles if the key exists (width can be a string on web, but not native, unless it's from the theme). Maybe we just add in all RN style options here.
  • Add option to enforce strict types from RN styles mapped with theme (padding can be $1 from the theme and 1, but not "1" if that's not in the theme)
  • Log error from Dripsy if you pass "" as a style?
  • Ensure that Tokenize is performant for large themes
  • Test in large monorepo

Performance 🦦

  • Memoize sx prop under the hood
  • Improve Gradient memoization

Features 🛩

  • Add support for Pressable's factory style={interaction => ({ ... })} prop
  • Add textShadows to theme
  • Add shadows to theme with RN style
  • Add transform to sx prop
  • Add boxShadow to sx prop with intellisense
  • Add textShadow to sx prop with intellisense
  • Suport React Native 0.65

Upgrade Guide

Install v3

yarn add dripsy@canary

# if you use this package, upgrade it too
yarn add @dripsy/gradient@canary

Versions

Make sure you have at least TypeScript 4.4+:

yarn add -D typescript

Add makeTheme

All you have to do to upgrade is wrap your theme with makeTheme, and then use module declaration.

import { makeTheme } from 'dripsy'

const theme = makeTheme({
  colors: { primary: 'blue' }
})

type MyTheme = typeof theme

declare module 'dripsy' {
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
  interface DripsyCustomTheme extends MyTheme {}
}

You now get intellisense 🥳

Gotchas

If you don't see autocomplete, there could be a few reasons.

Possibility 1. Your theme uses non-strict objects:

// ⛔️ typescript doesn't know the types here
const colors = {
  primary: 'blue'
}

const theme = makeTheme({ colors })

To fix this, either use as const for colors, or write it inline:

// ✅ as const
const colors = {
  primary: 'blue'
} as const

const theme = makeTheme({ colors })

// ✅ better yet, write inline
const theme = makeTheme({
  colors: {
    primary: 'blue'
  }
})

Still not getting intellisense?

If it still isn't working, please open an issue! But it should work now.

Strict Types

If you want to enforce strict types for your sx prop, you can do so with the new theme.types field:

const theme = makeTheme({
  // ...your theme here
  types: {
    onlyAllowThemeValues: 'always' // default: 'never'
  }
})

By setting types.onlyAllowThemeValues to always, you restrict yourself to only using theme values in your sx prop. While this may feel like overkill, it is a good pattern of enforcing consistent design.

This will only apply to theme values that are set. For example, if you haven't set the theme.space property, then strict types won't enforce for padding, margin, etc.

Incremental strict types

It's possible you want to implement strict types on a per-scale basis. The types.onlyAllowThemeValues also accepts an object with per-scale definitions.

const theme = makeTheme({
  space: {
    $0: 0,
    $1: 4,
    $2: 8,
    $3: 16,
    $4: 32,
    $5: 64,
    $6: 128,
    $7: 256,
    $8: 512
  },
  types: {
    onlyAllowThemeValues: {
      space: 'always'
    }
  }
})

Now, our sx prop's space-related properties (such as margin, padding, etc) will only accept one of the values in theme.space. To take full advantage of this feature, it is recommended that you don't use arrays, and instead make a dictionary with $ prefixes, such as the space one above.

Recommendation

The strict types feature is especially useful if you want to upgrade to Dripsy 3, have no breaking changes with your types, but slowly transition your app to have stricter types.

It also makes changing your theme much easier. For example, imagine that you change your space from an array to a dictionary:

const theme = makeTheme({
  // before
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
  // after
  space: {
    $0: 0,
    $1: 4,
    $2: 8,
    $3: 16,
    $4: 32,
    $5: 64,
    $6: 128,
    $7: 256,
    $8: 512
  }
})

After changing your theme.space, you likely have hundreds of invalid styles throughout your app. Finding them all would be a mess.

However, with incremental strict types, you could achieve this code refactor in seconds.

const theme = makeTheme({
  // before
  space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
  // after
  space: {
    $0: 0,
    $1: 4,
    $2: 8,
    $3: 16,
    $4: 32,
    $5: 64,
    $6: 128,
    $7: 256,
    $8: 512
  },
  types: {
    onlyAllowThemeValues: {
      space: 'always'
    }
  }
})

Now, simply run your TypeScript type-checker (yarn tsc --noEmit) to see all the places you need to update. You'll get useful errors like this:

Screen Shot 2021-10-04 at 11 10 26 AM

If you want an escape hatch that lets you style without theme keys without disabling strict types, use the style prop instead of sx.

Enforce React Native types

default: false

const theme = makeTheme({
  // ... theme
  types: {
    reactNativeTypesOnly: true // default false
  }
})

If you set this field to true, then you can only pass styles that fit a React Native style object.

For example, on web, you can set fontWeight as a number, like so: fontWeight: 400. With React Native, font weights must be strings (fontWeight: '400'). Using a number here would probably work with dripsy if you've utilized the customFonts feature in your theme.

I recommend setting this field to true. However, it will default to false to avoid breaking changes with users who are on v2 (like me) who don't want to refactor everywhere. If your app is just adding Dripsy, you should set this field to true. And it may work to set it to true either way. If it riddles your type checker with errors, then you can easily disable it.

To clarify: when this is false, it will allow either theme-ui types for a given field, or React Native types. When it's set to true, it will only allow React Native types (if they exist.)

Since React Native has no cursor property, for instance, using this field will always pull from the theme-ui web types.

image

Breaking Changes

There are essentially no breaking changes.

If you are a very advanced user with a custom setup, this might be breaking for you:

  • Removed @theme-ui/core dependency

    • For 99% of people, this won't matter. You likely didn't even know about this dependency. If that's the case, ignore this.
    • However, if you were using a very custom (undocumented) Dripsy setup with theme-ui on web, you'll need to wrap your app with theme-ui's ThemeProvider yourself. Previously, Dripsy used that provider under the hood.
  • Removed Button component (just import it from react-native instead of dripsy)

Array values

If your theme had any array values, turn them into objects.

Arrays in theme were added to v3!

@nandorojo
Copy link
Owner Author

nandorojo commented Sep 27, 2021

Okay, starting from scratch here.

import type { Theme } from '@theme-ui/css'
export interface DripsyBaseTheme extends Omit<Theme, 'fonts'> {
  customFonts?: { [key: string | number]: Record<string, string> }
  fonts?: Partial<Record<'root', string>> & Partial<Record<string, string>>
}

export interface DripsyCustomTheme {
  //
}

export interface DripsyFinalTheme extends DripsyBaseTheme, DripsyCustomTheme {}

And then

type SxProp = {
  [key in keyof ThemeUICSSProperties]?: // | ThemeUICSSProperties[key]
  keyof DripsyFinalTheme
}

In this case, you can only use the dripsy theme keys.

Screen Shot 2021-09-27 at 2 24 58 PM

Ok cool. But when I add back ThemeUICSSProperties, I never get intellisense. Presumably because their TS types are so recursive and slow:

import type { ThemeUICSSProperties } from '@theme-ui/css'

type SxProp = {
  [key in keyof ThemeUICSSProperties]?:
    | ThemeUICSSProperties[key]
    | keyof DripsyFinalTheme
}

It recognizes that a certain type is wrong, but it does not give suggestions:

Screen Shot 2021-09-27 at 2 27 16 PM

This is solved (I think) by adding the & {} hack:

type SxProp = {
  [key in keyof ThemeUICSSProperties]?:
    | (ThemeUICSSProperties[key] & {})
    | keyof DripsyFinalTheme
}

Screen Shot 2021-09-27 at 2 29 03 PM

Good start...

@nandorojo
Copy link
Owner Author

nandorojo commented Sep 27, 2021

Screen Shot 2021-09-27 at 2 46 38 PM

Running into this issue here...

Screen Shot 2021-09-27 at 2 46 04 PM

@nandorojo
Copy link
Owner Author

Solved:

type Tokenize<Theme> = Extract<
  keyof {
    [Key in Extract<keyof Theme, 'string'> as Theme[Key] extends
      | string
      | number
      | ''
      | never
      | undefined
      | null
      ? `${Key}`
      : // if we're iterating over the key of the theme
        // for example, if key = 'colors'
        // and colors: { primary: '...' }
        // then we should allow either colors.primary, OR colors
        `${Key}.${Tokenize<Theme[Key]>}`]: true
  },
  string | number | '' | never | undefined | null
>

@nandorojo
Copy link
Owner Author

Got rid of array keys:

type HiddenArrayKeys = Exclude<keyof [], number>

type Tokenize<Theme> = Extract<
  keyof {
    [Key in Extract<keyof Theme, string | number> as Theme[Key] extends
      | string
      | number
      | ''
      | never
      | undefined
      | null
      ? Key extends HiddenArrayKeys
        ? `${number}`
        : `${Key}`
      : Theme[Key] extends undefined
      ? `${Key}`
      : // if we're iterating over the key of the theme
        // for example, if key = 'colors'
        // and colors: { primary: '...' }
        // then we should allow either colors.primary, OR colors
        `${Key}.${Tokenize<Theme[Key]>}`]: true
  },
  string | number | '' | never | undefined | null
>

@nandorojo
Copy link
Owner Author

@nandorojo
Copy link
Owner Author

Screen Shot 2021-09-27 at 5 12 04 PM

More progress...

@nandorojo nandorojo changed the title Typez v3 Sep 27, 2021
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
@nandorojo
Copy link
Owner Author

Screen Shot 2021-09-27 at 6 47 19 PM

Screen Shot 2021-09-27 at 6 47 11 PM

Gradient support working...

@nandorojo
Copy link
Owner Author

Screen Shot 2021-09-27 at 7 10 32 PM

Added sx prop as a function support (not sure why tbh, but why not)

@nandorojo
Copy link
Owner Author

I published the first version of this at 3.0.0, would love help testing:

yarn add dripsy@canary

@nandorojo
Copy link
Owner Author

Added many new updates to the readme, including all things related to v3 and upgrading. https://github.com/nandorojo/dripsy/tree/typez

@nandorojo
Copy link
Owner Author

This is deployed at beatgig.com and battle tested. I'll do a full release on Tuesday. I'll be speaking at Next.js Conf about React Native + Next.js – don't miss it!

@nandorojo nandorojo merged commit fc9b3d2 into master Oct 26, 2021
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.

Custom theme-based TypeScript Intellisense
4 participants