Skip to content

Instantly share code, notes, and snippets.

@amiller-gh
Last active May 3, 2018 19:29
Show Gist options
  • Save amiller-gh/be31612bad6b4bff5a149d226aab9505 to your computer and use it in GitHub Desktop.
Save amiller-gh/be31612bad6b4bff5a149d226aab9505 to your computer and use it in GitHub Desktop.
CSS Blocks JSX API Concepts
:scope {
/* ... */
}
:scope[state|size=small] {
/* ... */
}
:scope[state|size=large] {
/* ... */
}
:scope {
/* ... */
}
:scope[state|isActive] {
/* ... */
}
:scope[state|size=small] {
/* ... */
}
:scope[state|size=large] {
/* ... */
}
.klass {
/* ... */
}
.klass[state|enabled] {
/* ... */
}
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