css-blocks
css-blocks copied to clipboard
TypeScript type checking for css-blocks as imported modules.
Each CSS block file can be represented as a collection of TypeScript classes.
.root { block-name: a-css-block; }
[state|a-block-state] {}
.a-thing {}
.a-thing[state|active] {}
.a-thing[state|my-theme=red] {}
.a-thing[state|my-theme=blue] {}
// these would be defined statically for all blocks and imported
type OptimizedClassNames = string[];
export interface BlockClass {
}
export type BooleanBlockState = () => OptimizedClassNames;
export type ExclusiveBlockState = (value: string) => OptimizedClassNames;
export type BlockState = BooleanBlockState | ExclusiveBlockState;
// type for this block
export class ACssBlock implements BlockClass {
root: this;
active: () => OptimizedClassNames;
"a-thing": ACssBlock.AThingClass;
[name: string]: BlockClass | BlockState;
}
export namespace ACssBlock {
export class AThingClass implements BlockClass {
[name: string]: BlockState;
"my-theme": (value: "red" | "blue") => OptimizedClassNames;
}
}
// usage example:
function foo(a: ACssBlock): OptimizedClassNames {
return a["a-thing"]["my-theme"]("red");
}
TODO:
- [ ] Further examples of block interfaces and inheritance are needed.
- [ ] Figure out how to create indexed types with only a limited set of indexed names.
Just swinging by many years down the line with a hint on how some of the type issues can be solved:
type MyBlockNameMap = {
readonly [K in 'foo' | 'bar']: readonly string[];
}
type MyBlockFunctions = {
color(name: 'red' | 'blue'): readonly string[];
}
type MyBlock = MyBlockNameMap & MyBlockFunctions;
let styles: MyBlock = null as any;
let bar: readonly string[] = styles.bar;
let foo: readonly string[] = styles.foo;
let red: readonly string[] = styles.color('red');
let blue: readonly string[] = styles.color('blue');