CSSDevConf San Antonio
David Khourshid @davidkpiano
&
vs.
vs.
&
BUT THAT'S A GOOD THING
Powerful languages inhibit information reuse.
Use the least powerful language suitable for expressing information, constraints or programs on the WWW.
PRINCIPLE
GOOD PRACTICE
✅
box-sizing: soap-box;
STREAM
VALUES
CUSTOM PROPERTIES
REACTIVE STYLES
CUSTOM PROPERTIES
CUSTOM PROPERTIES
:root {
--my-color: white;
}
button {
background-color: var(--my-color, blue);
}
button.special {
--my-color: red;
}
SPECIFICITY
INHERITANCE
CUSTOM PROPERTIES
element.style
.setProperty('--my-color', 'rebeccapurple');
element.style
.getPropertyValue('--my-color');
// => 'rebeccapurple'
element.style
.removeProperty('--my-color');
BROWSER SUPPORT
They're working on it!
PREPROCESSOR SUPPORT
const docStyle = document.documentElement.style;
const someElement = document.querySelector(...);
document.addEventListener('mousemove', (e) => {
someElement.style.transform = `
translateX(${e.clientX}px)
translateY(${e.clientY}px)
`;
});
JAVASCRIPT
const docStyle = document.documentElement.style;
document.addEventListener('mousemove', (e) => {
docStyle.setProperty('--mouse-x', e.clientX);
docStyle.setProperty('--mouse-y', e.clientY);
});
.ball {
transform:
translateX(calc(var(--mouse-x) * 1px))
translateY(calc(var(--mouse-y) * 1px));
}
JAVASCRIPT
CSS
APP
STATE
MOUSE
TOUCH
AUDIO
ASYNC DATA
OTHER ANIMATIONS
CSS VARIABLES
[
]
ARRAYS
1s
3s
4s
6.5s
9.5s
STREAMS
Rx.Observable
.from([1, 2, 3, 4, 5]);
Rx.Observable
.fromEvent(document, 'mousemove');
Rx.Observable
.fromEventPattern((handler) =>
hammerFoo.on('pan', handler));
const ball = document.querySelector('#ball');
const hBall = new Hammer(ball);
const pan$ = Rx.Observable
.fromEventPattern((handler) =>
hBall.on('pan', handler));
pan$.subscribe((event) => {
// do anything you want
});
pan$.subscribe(
handleNext,
handleError,
handleComplete
);
const tick$ = Rx.Observable
.interval(1, Rx.Scheduler.animationFrame);
Aggregate, All, Amb, and_, And, Any, apply, as_blocking, asObservable, AssertEqual, asyncAction, asyncFunc, Average, averageDouble, averageFloat, averageInteger, averageLong, blocking, Buffer, bufferWithCount, bufferWithTime, bufferWithTimeOrCount, byLine, cache, case, Cast, Catch, catchError, catchException, collect, collect (RxScala version of Filter), CombineLatest, combineLatestWith, Concat, concat_all, concatMap, concatMapObserver, concatMapTo, concatAll, concatWith, Connect, connect_forever, cons, Contains, controlled, Count, countLong, Create, cycle, Debounce, decode, DefaultIfEmpty, Defer, deferFuture, Delay, delaySubscription, delayWithSelector, Dematerialize, Distinct, distinctKey, distinctUntilChanged, distinctUntilKeyChanged, Do, doAction, doAfterTerminate, doOnCompleted, doOnEach, doOnError, doOnRequest, doOnSubscribe, doOnTerminate, doOnUnsubscribe, doseq, doWhile, drop, dropRight, dropUntil, dropWhile, ElementAt, ElementAtOrDefault, Empty, emptyObservable, empty?, encode, ensures, error, every, exclusive, exists, expand, failWith, Filter, filterNot, Finally, finallyAction, finallyDo, find, findIndex, First, FirstOrDefault, firstOrElse, FlatMap, flatMapFirst, flatMapIterable, flatMapIterableWith, flatMapLatest, flatMapObserver, flatMapWith, flatMapWithMaxConcurrent, flat_map_with_index, flatten, flattenDelayError, foldl, foldLeft, for, forall, ForEach, forEachFuture, forIn, forkJoin, From, fromAction, fromArray, FromAsyncPattern, fromCallable, fromCallback, FromEvent, FromEventPattern, fromFunc0, from_future, from_iterable, fromIterator, from_list, fromNodeCallback, fromPromise, fromRunnable, Generate, generateWithAbsoluteTime, generateWithRelativeTime, generator, GetEnumerator, getIterator, GroupBy, GroupByUntil, GroupJoin, head, headOption, headOrElse, if, ifThen, IgnoreElements, indexOf, interleave, interpose, Interval, into, isEmpty, items, Join, join (string), jortSort, jortSortUntil, Just, keep, keep-indexed, Last, lastOption, LastOrDefault, lastOrElse, Latest, latest (Rx.rb version of Switch), length, let, letBind, limit, LongCount, ManySelect, Map, map (RxClojure version of Zip), MapCat, mapCat (RxClojure version of Zip), map-indexed, mapTo, mapWithIndex, Materialize, Max, MaxBy, Merge, mergeAll, merge_concurrent, mergeDelayError, mergeObservable, mergeWith, Min, MinBy, MostRecent, Multicast, multicastWithSelector, nest, Never, Next, Next (BlockingObservable version), none, nonEmpty, nth, ObserveOn, ObserveOnDispatcher, observeSingleOn, of, of_array, ofArrayChanges, of_enumerable, of_enumerator, ofObjectChanges, OfType, ofWithScheduler, onBackpressureBlock, onBackpressureBuffer, onBackpressureDrop, OnErrorResumeNext, onErrorReturn, onExceptionResumeNext, orElse, pairs, pairwise, partition, partition-all, pausable, pausableBuffered, pluck, product, Publish, PublishLast, publish_synchronized, publishValue, raise_error, Range, Reduce, reductions, RefCount, Repeat, repeat_infinitely, repeatWhen, Replay, rescue_error, rest, Retry, retry_infinitely, retryWhen, Return, returnElement, returnValue, runAsync, Sample, Scan, scope, Select (alternate name of Map), select (alternate name of Filter), selectConcat, selectConcatObserver, SelectMany, selectManyObserver, select_switch, selectSwitch, selectSwitchFirst, selectWithMaxConcurrent, select_with_index, seq, SequenceEqual, sequence_eql?, SequenceEqualWith, Serialize, share, shareReplay, shareValue, Single, SingleOrDefault, singleOption, singleOrElse, size, Skip, SkipLast, skipLastWithTime, SkipUntil, skipUntilWithTime, SkipWhile, skipWhileWithIndex, skip_with_time, slice, sliding, slidingBuffer, some, sort, sort-by, sorted-list-by, split, split-with, Start, startAsync, startFuture, StartWith, startWithArray, stringConcat, stopAndWait, subscribe, SubscribeOn, SubscribeOnDispatcher, subscribeOnCompleted, subscribeOnError, subscribeOnNext, Sum, sumDouble, sumFloat, sumInteger, sumLong, Switch, switchCase, switchIfEmpty, switchLatest, switchMap, switchOnNext, Synchronize, Take, take_with_time, takeFirst, TakeLast, takeLastBuffer, takeLastBufferWithTime, takeLastWithTime, takeRight (see also: TakeLast), TakeUntil, takeUntilWithTime, TakeWhile, takeWhileWithIndex, tail, tap, tapOnCompleted, tapOnError, tapOnNext, Then, thenDo, Throttle, throttleFirst, throttleLast, throttleWithSelector, throttleWithTimeout, Throw, throwError, throwException, TimeInterval, Timeout, timeoutWithSelector, Timer, Timestamp, To, to_a, ToArray, ToAsync, toBlocking, toBuffer, to_dict, ToDictionary, ToEnumerable, ToEvent, ToEventPattern, ToFuture, to_h, toIndexedSeq, toIterable, toIterator, ToList, ToLookup, toMap, toMultiMap, ToObservable, toSet, toSortedList, toStream, ToTask, toTraversable, toVector, tumbling, tumblingBuffer, unsubscribeOn, Using, When, Where, while, whileDo, Window, windowWithCount, windowWithTime, windowWithTimeOrCount, windowed, withFilter, withLatestFrom, Zip, zipArray, zipWith, zipWithIndex
.filter(ball => ball.color === 'green')
.map(ball => makeSquare(ball))
.throttle(1000)
.scan((a, b) => a + b, 0)
1
2
3
5
6
8
1
1
1
2
1
2
If you can read this, the wifi sucks
A "reactive animation" is one involving discrete changes, due to events.
By allowing programmers to express the "what" of an interactive animation, one can hope to then automate the "how" of its presentation.
If you can read this, the wifi still sucks
Rx.Subject()
CSS Variables
mouseMove$
scroll$
tap$
swipe$
timer$
...etc.
(Observers)
An
Rx.Subject is both
an
observable and an
observer.
const mouse$ = Rx.Observable
.fromEvent(document, 'mousemove')
.map(({ x, y }) => ({ x, y }));
const style$ = RxCSS({
mouse: mouse$,
});
style$.subscribe(...);
:root {
--mouse-x: 0;
--mouse-y: 0;
}
.ball {
transform:
translate(calc(var(--mouse-x) * 1px))
translateY(calc(var(--mouse-y) * 1px));
– scroll$ Observable
const content = document
.querySelector('.content');
const scroll$ = Rx.Observable
.fromEvent(content, 'scroll')
.map(({target}) =>
2 * target.scrollTop / target.clientHeight);
RxCSS({
scroll: scroll$,
});
.content {
opacity: calc(1 - var(--scroll));
transform: scale(calc(1 + var(--scroll) / 3));
}
Oh my god
Please wifi just work
😭
wifi y u do dis
RxCSS({
mouse: mouse$,
pan: pan$,
// ...
}).subscribe((values) => {
// do anything!
});