Last active
May 3, 2018 19:29
-
-
Save amiller-gh/be31612bad6b4bff5a149d226aab9505 to your computer and use it in GitHub Desktop.
CSS Blocks JSX API Concepts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:scope { | |
/* ... */ | |
} | |
:scope[state|size=small] { | |
/* ... */ | |
} | |
:scope[state|size=large] { | |
/* ... */ | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:scope { | |
/* ... */ | |
} | |
:scope[state|isActive] { | |
/* ... */ | |
} | |
:scope[state|size=small] { | |
/* ... */ | |
} | |
:scope[state|size=large] { | |
/* ... */ | |
} | |
.klass { | |
/* ... */ | |
} | |
.klass[state|enabled] { | |
/* ... */ | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import objstr from "obj-str"; | |
import typography from "./typography.block.css"; | |
import styles from "./component.block.css"; | |
// Current: Fake Functional API | |
// ============================== | |
// This API has requires a lot of validation that users aren't mis-using | |
// the fake functions delivered. It is easier to abuse and harder to | |
// explain the restrictions. It also doesn't *feel* like you're writing | |
// HTML that matches the block written – states are written as attribute | |
// selectors, these directly imply they are consumed as class names. | |
function render({isActive, size}){ | |
let rootClasses = objstr({ | |
[styles]: true, | |
[styles.isActive()]: isActive, | |
[styles.size(size)]: true, | |
[typography]: true, | |
[typography.size(size)]: true | |
}); | |
let subElementClasses = objstr({ | |
[styles.klass]: true, | |
[styles.klass.enabled()]: true | |
}); | |
return ( | |
<div className={rootClasses}> | |
<div className={subElementClasses}>What a beautiful component</div> | |
</div> | |
); | |
} | |
// Concept: Declarative State API | |
// ============================= | |
// Here we are able to refer to state selectors as they are | |
// written – as namespaced attributes. In order to fully | |
// resolve the state value to a specific block file, they | |
// must be referred to as a property on an imported block. | |
// Because they are used with the `state:` namespace prefix, | |
// we no longer need the "property vs method" distinction | |
// between classes and states used above. | |
// But! JSX does not currently allow member expressions in | |
// attribute names... | |
// https://github.com/facebook/jsx/issues/21#issuecomment-307663820 | |
// This also does not completly rid our API of functional code. | |
// Applying `:scope` or class names to elements still requires some | |
// functional utility. | |
function render({isActive, size}){ | |
return ( | |
<div | |
className={objstr({ [styles]: true, [typography]: true })} // We still need something functional for class names... | |
state:styles.isActive={isActive} | |
state:styles.size={size} | |
state:typography.size={size} | |
> | |
<div | |
className={styles.klass} | |
state:styles.klass.enabled={true} | |
>What a beautiful component</div> | |
</div> | |
); | |
} | |
// Concept: Declarative State API, No Namespace | |
// (Not Possible, For Illustration Only) | |
// ============================================ | |
// Here we are able to refer to state selectors as they are | |
// written – as namespaced attributes. In this syntax we don't | |
// differentiate between blocks in state attributes. This creates | |
// ambiguity in what state you are referring to. In this case, both | |
// the component and typography blocks have a `size` state. If these | |
// blocks are both delivered from third parties, and deliver vastly | |
// different sub-state values for the same state attribute, this may create an | |
// impossible integration situation for the consumer. Because of this, we can | |
// show that using attributes *without* member expressions would place | |
// undue burdon on the consumer and make the framework much more | |
// difficult to statically analyze. | |
function render({isActive, size}){ | |
return ( | |
<div | |
className={objstr({ [styles]: true, [typography]: true })} // We still need something functional for class names... | |
state:isActive={isActive} | |
state:size={size} // Which "size" attribute are you referencing here!? What if they have different APIs?! | |
> | |
<div | |
className={styles.klass} | |
state:enabled={true} | |
>What a beautiful component</div> | |
</div> | |
); | |
} | |
// Fully Declarative API Concept | |
// ============================= | |
// We can remove *all* functional complexity from applying classes and states | |
// to an element if we choose a similar attribute-based API for applying `:scope`s | |
// and classes. This API eliminates the need to import a string concatenation utility, | |
// and further reduces the complexity that our Analyzers and Rewriters need to manage. | |
// However, an API in this vein may feel odd at first, because you're not using | |
// classes exactly as written (not that we are in the current API). | |
function render({isActive, size}){ | |
return ( | |
<div | |
class:style={true} // We can possibly apply classes the same way we apply states | |
class:typography={true} | |
state:styles.isActive={isActive} | |
state:styles.size={size} | |
state:typography.size={size} | |
> | |
<div | |
class:style.klass={true} | |
state:style.klass.enabled={true} | |
>What a beautiful component</div> | |
</div> | |
); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment