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

Proposal: stricter / app-specific CSSNestedProperties #351

Open
silviogutierrez opened this issue Mar 15, 2020 · 0 comments
Open

Proposal: stricter / app-specific CSSNestedProperties #351

silviogutierrez opened this issue Mar 15, 2020 · 0 comments

Comments

@silviogutierrez
Copy link

silviogutierrez commented Mar 15, 2020

First off: huge fan of this library. I can't praise it enough! So simple and really makes styling an app a pleasure.

TypeStyle already has a way to add properties that are missing, as documented here:

https://github.com/typestyle/typestyle.github.io/blob/source/src/docs/core.md#tip-declaring-new-css-stuff

But it lacks a way to further restrict properties. Why restrict them? Two use cases:

  • Forbid using specific properties
  • Only allow certain colors, fonts, weights, etc based on a style guide.

A lot of the above can be enforced procedurally through mixins, code review, etc. But why not do it at the compiler level?

So I actually got this to work with a few hacks, and it's 90% of the way there. But with a few tweaks it can be built-in to TypeStyle. And far simpler.

In the example below, as a POC, I wanted to limit font weights and font families only to what I provide.

// Put this in a file like client/style.ts
import * as typeCSSTips from "csstips";

import {
    media as typeMedia,
    style as typeStyle,
    types,
} from "typestyle";


type CSSTipNames = Extract<keyof typeof typeCSSTips, string>;

type HandleCSSTipMember<T> = T extends (... args: infer S) => types.NestedCSSProperties ? (...args: S) => JoyNestedCSSProperties : JoyNestedCSSProperties;

type JoyCSSTips = {
    [P in Exclude<CSSTipNames, 'padding' |' margin' | 'border'>]: HandleCSSTipMember<typeof typeCSSTips[P]>;
} & {
    [P in Extract<CSSTipNames, 'padding' |' margin' | 'border'>]: typeof typeCSSTips[P];
};

export const csstips = typeCSSTips as JoyCSSTips;

export interface JoyNestedCSSProperties
    extends Omit<types.NestedCSSProperties, "fontFamily" | "fontWeight"> {
    fontFamily?: "Montserrat";
    fontWeight?: 200 | 300 | 500 | 600;
   
    // this could be tighter but I just wanted to demonstrate 
    $nest?: {
        [selector: string]: JoyNestedCSSProperties | undefined;
    };
}
export function style(...objects: (JoyNestedCSSProperties | undefined)[]): string;
export function style(
    ...objects: (JoyNestedCSSProperties | null | false | undefined)[]
): string;
export function style() {
    return typeStyle.apply(undefined, arguments);
}

export const media = (
    mediaQuery: types.MediaQuery,
    ...objects: (JoyNestedCSSProperties | undefined | null | false)[]
): JoyNestedCSSProperties => {
    return typeMedia(mediaQuery, ...objects) as JoyNestedCSSProperties;
};

The main style function was super easy. But it could be even easier: just make typestyle.createTypeStyle(); take in a generic and pass it to a class. Like so:

declare function createTypeStyle<T = types.NestedCSSProperties>()

It still defaults for those who don't care.

We'd also need to move media under that factory and any related functions. But the above covered everything I used in my code base.

CSS Tips
Unfortunately, CSS tips were a little harder, since it's a collection of functions and objects that return CSSNestedProperties. Boxed types are a particularly troublesome one because argument inference in TS makes all of these required, which isn't true in the real source.

But again, if we make these into a factory and simply provide a default instance just like TypeStyle, it should be no problem.

Thanks again for a great library!

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

No branches or pull requests

1 participant