Hello, my name is Dmitry Karlovsky and I am ... the author of one of the first frameworks entirely written in typescript - $ mol . It makes the most of static typing capabilities. And today we will talk about the most rigid static fixing of styles.
This is a transcript of a talk at PiterJS # 46 . You can either watch a video recording , or open it in the presentation interface , or read as an article ...
$my_profile, : . : .
..
$my_profile $mol_view sub /
<= Menu $my_panel
<= Details $my_panel
$my_panel $mol_view sub /
<= Head $mol_view
<= Body $mol_scroll
interface $my_profile extends $mol_view {
Menu(): $my_panel
Details(): $my_panel
} )
interface $my_panel extends $mol_view {
Head(): $mol_view
Body(): $mol_scroll
} )
— , . , , . .
DOM
, , DOM-. , DOM-. dom_name
. , , . , , , .
interface $my_profile extends $mol_view {
Menu(): $my_panel
Details(): $my_panel
} )
interface $my_panel extends $mol_view {
Head(): $mol_view
Body(): $mol_scroll
} )
<mol_view
mol_view
my_panel_body
my_profile_details_body
>
<mol_button_major
mol_view
mol_button
mol_button_major
my_profile_signup
>
:
mol_view — , , , reset , , .
my_panel_body — Body $my_panel.
my_profile_details_body — $my_panel Details $my_profile.
, . , , .
<mol_view
mol_view
my_panel_body
my_profile_details_body
>
<mol_button_major
mol_view
mol_button
mol_button_major
my_profile_signup
>
[my_profile_details_body] {
overflow: 'overlay';
}
[my_profile_details_body] [mol_button] {
border-radius: .5rem;
}
CSS . , . , . , .
CSS.
[my_profile_details_body] {
overflow: 'overlay';
}
[my_profile_details_body] [mol_button] {
border-radius: .5rem;
}
$mol_style_define( $my_profile , {
Details: {
Body: {
overflow: 'overlay',
$mol_button: {
border: {
radius: rem(.5),
},
},
},
},
} )
$mol_style_define, StyleSheet. $my_profile JSON, , Details Body -, — -.
CSSOM: DevTools
: CSSOM, CSS style
. , Chrome Dev Tools , . .
, aphrodite, — CSS. , , $mol_view.
CSS — . 3 . ..
CSSStyleDeclaration:
CSSStyleDeclaration
, . 500 , string
.
type CSSStyleDeclaration = {
display: string
// 500 lines
}
, - , .
{
display: 'black' //
}
csstype:
type DisplayProperty =
| Globals
| DisplayOutside
| DisplayInside
| DisplayInternal
| DisplayLegacy
| "contents" | "list-item" | "none"
| string
, , ..
type DisplayProperty = string
, , .
csstype@3:
, "" — string
, - :
type Display =
| Globals
| DisplayOutside
| DisplayInside
| DisplayInternal
| DisplayLegacy
| "contents" | "list-item" | "none"
| (string & {})
, ..
{
display: 'black' //
}
, . Properties
..
interface Properties {
/**
* Whether an element is treated as a block or inline element
* and the layout used for its children, such as flow layout, grid or flex.
*/
display?: 'block' | 'inline' | 'none' | ... | Common
// etc
}
type Common = 'inherit' | 'initial' | 'unset'
, , , 500 CSS-.
CSS , ..
overflow: {
x: 'auto' ,
y: 'scroll',
anchor: 'none',
}
interface Properties {
overflow? : {
x?: Overflow | Common
y?: Overflow | Common
anchor?: 'auto' | 'none' | Common
}
}
type Overflow = 'visible' | 'hidden' | ... | Common
, (1px
, 2rem
) (calc(1rem + 1px)
).
interface Properties {
width?: Size
height?: Size
}
type Size =
| 'auto' | 'max-content' | 'min-content' | 'fit-content'
| Length | Common
type Length = 0 | Unit< 'rem' | ... | '%' > | Func<'calc'>
, .
class Unit< Lit extends string > {
constructor(
readonly val: number,
readonly lit: Lit,
) { }
toString() {
return this.val + this.lit
}
}
, , , , , .
function rem( val : number ) {
return new Unit( val , 'rem' )
}
{
width: rem(1) // Unit<'rem'>
}
. , .
Func<
Name extends string,
Value = unknown,
> {
constructor(
readonly name: Name,
readonly val: Value,
) { }
toString() {
return `${ this.name }(${ this.val })`
}
}
function calc( val : string ) {
return new Func( 'calc' , val )
}
{
// Func< 'calc' , string >
width: calc( '1px + 1em' )
}
CSS . , margin
1 4 . 1 2 - , : 4, margin-left
— , 3, . , , — . , .
interface Properties {
margin?: Directions<Length>
padding?: Directions<Length>
}
type Directions< Value > =
| Value
| [ Value , Value ]
| {
top?: Value ,
right?: Value ,
bottom?: Value ,
left?: Value ,
}
margin: rem(.5)
padding: [ 0 , rem(.5) ]
margin: {
top: 0,
right: rem(.5),
bottom: rem(.5),
left: rem(.5),
}
$mol_colors — . ..
type Color =
| keyof typeof $mol_colors
| 'transparent' | 'currentcolor'
| $mol_style_func< 'hsla' | 'rgba' | 'var' >
color?: Color | Common
{
color: 'snow',
background: {
color: hsla( 0 , 0 , 50 , .1 ),
},
}
hsl
rgb
, hsla
rgba
— , .
, . , ..
background?: {
image?: [ $mol_style_func<'url'> ][]
}
background: {
image: [
[url('/foo.svg')],
[url('/bar.svg')],
],
},
, , : , . (, ), . .
— ..
box?: {
shadow?: readonly {
inset: boolean
x: Length
y: Length
blur: Length
spread: Length
color: Color
}[]
}
box: {
shadow: [
{
inset: true,
x: 0,
y: 0,
blur: rem(.5),
spread: 0,
color: 'black',
},
],
},
, , . , . — , , , , .
-
, . $my_profile, Details, $my_panel, Body, $mol_scroll. , , $my_profile.
interface $my_profile {
Details(): $my_panel
} )
interface $my_panel {
Body(): $mol_scroll
} )
$mol_style_define( $my_profile , {
padding: rem(1),
Details: {
margin: 'auto',
Body: {
overflow: 'scroll',
},
},
} )
, , .
-
$mol_type_pick, , , , .
$mol_type_pick<
$my_profile,
()=> $mol_view,
>
interface $my_profile extends $mol_view {
title(): string
Menu(): $my_panel
Details(): $my_panel
} )
⇩
{
Menu(): $my_panel
Details(): $my_panel
}
, title
, . . Menu
Details
.
-
, . , , — .
interface $my_profile {
Menu(): $my_panel
Details(): $my_panel
} )
interface $mol_button {
} )
$mol_style_define( $my_profile , {
$mol_button: {
color: 'red',
},
Details: {
$mol_button: {
color: 'blue',
},
},
} )
, , $
. $mol_type_pick, .
type $mol_view_all = $mol_type_pick<
typeof $,
$mol_view,
>
namespace $ {
export class $mol_view {}
export class $my_panel {}
export class $mol_tree {}
}
⇩
{
$mol_view: typeof $mol_view
$my_panel: typeof $my_panel
}
, - . — .
function $mol_style_define<
View extends typeof $mol_view,
Config extends Styles< View >
>(
view : View,
config : Config
)
type Config< View extends $mol_view > = {
$my_panel: {
$my_panel: {
...
}
$my_deck: {
$my_panel: {
...
}
}
...
}
...
}
, , . . , .
, , .
, JSON, , . .
function $mol_style_define<
View extends typeof $mol_view,
Config extends StylesGuard<
View,
Config,
>
>(
view: View,
config: Config
)
{
Details: {
foo: 'bar',
},
}
⇩
{
Details: {
foo: never,
},
}
. , , .
never , , , .
never
, .
, , , — - , , , .
, . $mol_view .
attr *
^
mol_link_current <= current false
⇩
attr() {
return {
... super.attr(),
mol_link_current: this.current(),
}
}
: -> -> -> (, ).
{
'@': {
mol_link_current: {
true: {
zIndex: 1
}
}
}
}
, ..
{
':hover': {
zIndex: 1
}
}
-. , CSS, - .
{
$mol_scroll: {
overflow: 'scroll',
'@media': {
'print': {
overflow: 'visible',
},
},
},
}
, .
{
'>': {
$mol_view: {
margin: rem(1),
},
$mol_button: {
margin: rem(0),
},
},
}
- ( )
- ( 10 500)
- calc ( )
$mol
, $mol, mol_style_all
, CSS-.
import {
$mol_style_unit,
$mol_style_func,
$mol_style_properties,
} from 'mol_style_all'
const { em , rem } = $mol_style_unit
const { calc } = $mol_style_func
const props : $mol_style_properties = {
margin: [ em(1) , rem(1) ],
height: calc('100% - 1rem'),
}
codesandbox.io/s/molstyleall-ked9t
, , CSS, , $mol_view. , , mam_mol — , .
...
, , . , , , ..
- eigenmethod/mol/style —
- mam_mol —
- nin_jin —
- habhub.hyoo.ru —
- _jinnin —
- . . , , , pixel-perfect.
- ! , .
- , .
- , .