Skip to content

Instantly share code, notes, and snippets.

@starryeyez024
Last active August 29, 2015 14:23
Show Gist options
  • Select an option

  • Save starryeyez024/6b7896875fb35916d550 to your computer and use it in GitHub Desktop.

Select an option

Save starryeyez024/6b7896875fb35916d550 to your computer and use it in GitHub Desktop.
Set theme in data attributes
<h1>Theming UI Components</h1>
<p>Each component needs to be aware that it may be used within a colour scheme so it needs to own the process
of creating the various classes required to support different uses. Sometimes semantic classes are the way forward,
but perhaps we should just allow the use of names to describe the particular style (think Solarized & Darcula, not Primary, Secondary, Success, etc.)</p>
<p>Here we create a themes map, which contains the colour definitions for a variety of named themes.
This makes it easier to re-use a colour scheme across components and removes the cases where
two component modifier classes (with different names) confusingly implement the same colour scheme.
e.g. no <code>.btn--primary</code> and <code>.panel--brand</code> which actually use the same colours</p>
<p>Read more about this approach in this blog post - <a href="http://www.ian-thomas.net/a-sassy-approach-to-theming-components/" class="link-t-sassmeister-dark">A Sassy Approach To Theming Components</a></p>
<hr class="rule"/>
<div class="band" data-theme="codepen">
<h2 class="heading">Codepen Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn" data-cta-type='primary'>I'm a primary call to action button</button>
<button class="btn" data-cta-type='secondary'>I'm a secondary call to action button</button>
<button class="btn" data-cta-type='disabled'>I'm a disabled button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
<div class="band" data-theme="codepen-light" >
<h2 class="heading">Codepen Light Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn">I'm a button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
<div class="band" data-theme="sassmeister">
<h2 class="heading">Sassmeister Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn">I'm a button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
<div class="band" data-theme="sassmeister-dark">
<h2 class="heading">Sassmeister Dark Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn">I'm a button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
// ----
// libsass (v3.2.5)
// ----
// Boilerplate code and basic styles
$color-primary: #fff;
$color-primary-background: #E86EA4;
body {
line-height: 1.5;
padding: 2% 10%;
color: #5B5B5B;
font-family: "Helvetica";
}
.band {
padding: 20px;
margin: 20px 0;
}
.note {
padding: 2em;
font-size: 1.2rem;
font-weight: 300;
margin: 2em 0 2em 1em;
}
.note--small {
font-size: 0.8rem;
padding: 0.5em 1em;
margin: 1em 0;
}
.note--inline {
display: inline-block;
}
.rule {
border: 0;
margin: 2em 0;
width: 100%;
height: 1px;
background-color: #ccc;
}
.btn {
display: block;
width: auto;
padding: 0.8em 1em;
margin: 0.8em 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial;
font-size: 1.2em;
border-width: 1px;
border-style: solid;
font-weight: bold;
text-transform: uppercase;
}
a {
font-weight: bold;
}
.thing {
color: green;
}
//////////////////////////
// THEMES
/**
Themes are all stored in a map data-structure which gives flexibilty
when traversing based on keys and also looking up values based on strings
*/
$themes: (
sassmeister: (
foreground: #efefef,
background: #E86EA4,
border: darken(#E86EA4, 10),
emphasis: darken(#FFFFFF, 10),
subtle: lighten(#E86EA4, 20),
highlight: lighten(#E86EA4, 10),
radius: 0
),
sassmeister-dark: (
foreground: #E86EA4,
background: #eee,
border: darken(#fff, 10),
emphasis: darken(#E86EA4, 10),
subtle: lighten(#E86EA4, 10),
highlight: darken(#fff, 10),
radius: 0
),
codepen: (
foreground: #CCCCCC,
background: #1D1F20,
border: #000000,
emphasis: darken(#999999, 10),
subtle: lighten(#999999, 10),
highlight: lighten(#1D1F20, 10),
radius: 3px
),
codepen-light: (
foreground: #1D1F20,
background: #e8e8e8,
border: darken(#F2F2F2, 15),
emphasis: lighten(#1D1F20, 10),
subtle: darken(#1D1F20, 10),
highlight: darken(#F2F2F2, 10),
radius: 3px
),
info: (
foreground: #FFFFFF,
background: #809BBD,
border: darken(#809BBD, 15),
subtle: darken(#FFFFFF, 10),
highlight: lighten(#809BBD, 10),
radius: 6px,
font-style: italic
)
);
@function get-theme($name) {
@return map-get($themes, $name);
}
/**
* This function should warn if a theme requirement isn't met,
* helping to catch any issues at compile time in development
*/
@function requires($themeName, $props) {
$theme: get-theme($themeName);
@each $key in $props {
@if (map-has-key($theme, $key) == false) {
@warn "To use this theme requires #{$key} to be set in the map";
@return false;
}
}
@return true;
}
//////////////////////////
// Band theme
$band-requirements: foreground;
@mixin band-theme($name) {
$theme: map-get($themes, $name);
.band {
&[data-theme="#{$name}"] {
background-color: map-get($theme, "foreground");
}
}
}
//////////////////////////
// Links
$link-requirements: foreground emphasis;
@mixin link-theme($name) {
$theme: get-theme($name);
.link {
[data-theme="#{$name}"] & {
color: map-get($theme, foreground);
&:hover, &:active {
color: map-get($theme, emphasis);
}
}
}
}
//////////////////////////
// Buttons
$button-requirements: foreground background border radius highlight emphasis;
@mixin button-theme($name) {
$theme: map-get($themes, $name);
.btn {
[data-theme="#{$name}"] & {
color: map-get($theme, "foreground");
background-color: map-get($theme, "background");
border-color: map-get($theme, "border");
border-radius: map-get($theme, "radius");
&:hover {
background-color: map-get($theme, "highlight");
}
&[data-cta-type='primary'] {
background-color: map-get($theme, "background");
border-color: map-get($theme, "border");
&:hover {
background-color: map-get($theme, "highlight");
}
}
&[data-cta-type='secondary'] {
background-color: map-get($theme, "emphasis");
border-color: map-get($theme, "subtle");
&:hover {
background-color: map-get($theme, "highlight");
}
}
&[data-cta-type='disabled'] {
background-color: map-get($theme, "subtle");
border-color: transparent;
}
}
}
}
//////////////////////////
// Notes
$note-requirements: background foreground subtle; // font-style is optional
@mixin note-theme($name) {
$theme: map-get($themes, $name);
.note {
[data-theme="#{$name}"] & {
background: map-get($theme, "subtle");
color: map-get($theme, "background");
@if map-has-key($theme, "font-style") {
font-style: map-get($theme, "font-style");
}
}
}
}
//////////////////////////
// Rule lines
$rule-requirements: background;
@mixin rule-theme($name) {
$theme: map-get($themes, $name);
.rule {
[data-theme="#{$name}"] & {
background-color: map-get($theme, "background");
}
}
}
//////////////////////////
// Headings
$heading-requirements: background;
@mixin heading-theme($name) {
$theme: map-get($themes, $name);
.heading {
[data-theme="#{$name}"] & {
color: map-get($theme, "background");
}
}
}
// Each component that opts into a theme should be included here
@each $themeName in map-keys($themes) {
@if (requires($themeName, $band-requirements)) {
@include band-theme($themeName);
}
@if (requires($themeName, $button-requirements)) {
@include button-theme($themeName);
}
@if (requires($themeName, $note-requirements)) {
@include note-theme($themeName);
}
@if (requires($themeName, $rule-requirements)) {
@include rule-theme($themeName);
}
@if (requires($themeName, $link-requirements)) {
@include link-theme($themeName);
}
@if (requires($themeName, $heading-requirements)) {
@include heading-theme($themeName);
}
}
body {
line-height: 1.5;
padding: 2% 10%;
color: #5B5B5B;
font-family: "Helvetica";
}
.band {
padding: 20px;
margin: 20px 0;
}
.note {
padding: 2em;
font-size: 1.2rem;
font-weight: 300;
margin: 2em 0 2em 1em;
}
.note--small {
font-size: 0.8rem;
padding: 0.5em 1em;
margin: 1em 0;
}
.note--inline {
display: inline-block;
}
.rule {
border: 0;
margin: 2em 0;
width: 100%;
height: 1px;
background-color: #ccc;
}
.btn {
display: block;
width: auto;
padding: 0.8em 1em;
margin: 0.8em 0;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial;
font-size: 1.2em;
border-width: 1px;
border-style: solid;
font-weight: bold;
text-transform: uppercase;
}
a {
font-weight: bold;
}
.thing {
color: green;
}
/**
Themes are all stored in a map data-structure which gives flexibilty
when traversing based on keys and also looking up values based on strings
*/
/**
* This function should warn if a theme requirement isn't met,
* helping to catch any issues at compile time in development
*/
.band[data-theme="sassmeister"] {
background-color: #efefef;
}
[data-theme="sassmeister"] .btn {
color: #efefef;
background-color: #E86EA4;
border-color: #e14288;
border-radius: 0;
}
[data-theme="sassmeister"] .btn:hover {
background-color: #ef9ac0;
}
[data-theme="sassmeister"] .btn[data-cta-type='primary'] {
background-color: #E86EA4;
border-color: #e14288;
}
[data-theme="sassmeister"] .btn[data-cta-type='primary']:hover {
background-color: #ef9ac0;
}
[data-theme="sassmeister"] .btn[data-cta-type='secondary'] {
background-color: #e6e6e6;
border-color: #f6c6db;
}
[data-theme="sassmeister"] .btn[data-cta-type='secondary']:hover {
background-color: #ef9ac0;
}
[data-theme="sassmeister"] .btn[data-cta-type='disabled'] {
background-color: #f6c6db;
border-color: transparent;
}
[data-theme="sassmeister"] .note {
background: #f6c6db;
color: #E86EA4;
}
[data-theme="sassmeister"] .rule {
background-color: #E86EA4;
}
[data-theme="sassmeister"] .link {
color: #efefef;
}
[data-theme="sassmeister"] .link:hover, [data-theme="sassmeister"] .link:active {
color: #e6e6e6;
}
[data-theme="sassmeister"] .heading {
color: #E86EA4;
}
.band[data-theme="sassmeister-dark"] {
background-color: #E86EA4;
}
[data-theme="sassmeister-dark"] .btn {
color: #E86EA4;
background-color: #eee;
border-color: #e6e6e6;
border-radius: 0;
}
[data-theme="sassmeister-dark"] .btn:hover {
background-color: #e6e6e6;
}
[data-theme="sassmeister-dark"] .btn[data-cta-type='primary'] {
background-color: #eee;
border-color: #e6e6e6;
}
[data-theme="sassmeister-dark"] .btn[data-cta-type='primary']:hover {
background-color: #e6e6e6;
}
[data-theme="sassmeister-dark"] .btn[data-cta-type='secondary'] {
background-color: #e14288;
border-color: #ef9ac0;
}
[data-theme="sassmeister-dark"] .btn[data-cta-type='secondary']:hover {
background-color: #e6e6e6;
}
[data-theme="sassmeister-dark"] .btn[data-cta-type='disabled'] {
background-color: #ef9ac0;
border-color: transparent;
}
[data-theme="sassmeister-dark"] .note {
background: #ef9ac0;
color: #eee;
}
[data-theme="sassmeister-dark"] .rule {
background-color: #eee;
}
[data-theme="sassmeister-dark"] .link {
color: #E86EA4;
}
[data-theme="sassmeister-dark"] .link:hover, [data-theme="sassmeister-dark"] .link:active {
color: #e14288;
}
[data-theme="sassmeister-dark"] .heading {
color: #eee;
}
.band[data-theme="codepen"] {
background-color: #CCCCCC;
}
[data-theme="codepen"] .btn {
color: #CCCCCC;
background-color: #1D1F20;
border-color: #000000;
border-radius: 3px;
}
[data-theme="codepen"] .btn:hover {
background-color: #35393b;
}
[data-theme="codepen"] .btn[data-cta-type='primary'] {
background-color: #1D1F20;
border-color: #000000;
}
[data-theme="codepen"] .btn[data-cta-type='primary']:hover {
background-color: #35393b;
}
[data-theme="codepen"] .btn[data-cta-type='secondary'] {
background-color: gray;
border-color: #b3b3b3;
}
[data-theme="codepen"] .btn[data-cta-type='secondary']:hover {
background-color: #35393b;
}
[data-theme="codepen"] .btn[data-cta-type='disabled'] {
background-color: #b3b3b3;
border-color: transparent;
}
[data-theme="codepen"] .note {
background: #b3b3b3;
color: #1D1F20;
}
[data-theme="codepen"] .rule {
background-color: #1D1F20;
}
[data-theme="codepen"] .link {
color: #CCCCCC;
}
[data-theme="codepen"] .link:hover, [data-theme="codepen"] .link:active {
color: gray;
}
[data-theme="codepen"] .heading {
color: #1D1F20;
}
.band[data-theme="codepen-light"] {
background-color: #1D1F20;
}
[data-theme="codepen-light"] .btn {
color: #1D1F20;
background-color: #e8e8e8;
border-color: #cccccc;
border-radius: 3px;
}
[data-theme="codepen-light"] .btn:hover {
background-color: #d8d8d8;
}
[data-theme="codepen-light"] .btn[data-cta-type='primary'] {
background-color: #e8e8e8;
border-color: #cccccc;
}
[data-theme="codepen-light"] .btn[data-cta-type='primary']:hover {
background-color: #d8d8d8;
}
[data-theme="codepen-light"] .btn[data-cta-type='secondary'] {
background-color: #35393b;
border-color: #050505;
}
[data-theme="codepen-light"] .btn[data-cta-type='secondary']:hover {
background-color: #d8d8d8;
}
[data-theme="codepen-light"] .btn[data-cta-type='disabled'] {
background-color: #050505;
border-color: transparent;
}
[data-theme="codepen-light"] .note {
background: #050505;
color: #e8e8e8;
}
[data-theme="codepen-light"] .rule {
background-color: #e8e8e8;
}
[data-theme="codepen-light"] .link {
color: #1D1F20;
}
[data-theme="codepen-light"] .link:hover, [data-theme="codepen-light"] .link:active {
color: #35393b;
}
[data-theme="codepen-light"] .heading {
color: #e8e8e8;
}
.band[data-theme="info"] {
background-color: #FFFFFF;
}
[data-theme="info"] .note {
background: #e6e6e6;
color: #809BBD;
font-style: italic;
}
[data-theme="info"] .rule {
background-color: #809BBD;
}
[data-theme="info"] .heading {
color: #809BBD;
}
<h1>Theming UI Components</h1>
<p>Each component needs to be aware that it may be used within a colour scheme so it needs to own the process
of creating the various classes required to support different uses. Sometimes semantic classes are the way forward,
but perhaps we should just allow the use of names to describe the particular style (think Solarized & Darcula, not Primary, Secondary, Success, etc.)</p>
<p>Here we create a themes map, which contains the colour definitions for a variety of named themes.
This makes it easier to re-use a colour scheme across components and removes the cases where
two component modifier classes (with different names) confusingly implement the same colour scheme.
e.g. no <code>.btn--primary</code> and <code>.panel--brand</code> which actually use the same colours</p>
<p>Read more about this approach in this blog post - <a href="http://www.ian-thomas.net/a-sassy-approach-to-theming-components/" class="link-t-sassmeister-dark">A Sassy Approach To Theming Components</a></p>
<hr class="rule"/>
<div class="band" data-theme="codepen">
<h2 class="heading">Codepen Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn" data-cta-type='primary'>I'm a primary call to action button</button>
<button class="btn" data-cta-type='secondary'>I'm a secondary call to action button</button>
<button class="btn" data-cta-type='disabled'>I'm a disabled button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
<div class="band" data-theme="codepen-light" >
<h2 class="heading">Codepen Light Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn">I'm a button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
<div class="band" data-theme="sassmeister">
<h2 class="heading">Sassmeister Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn">I'm a button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
<div class="band" data-theme="sassmeister-dark">
<h2 class="heading">Sassmeister Dark Theme</h2>
<blockquote class="note">Names such as Primary, Secondary, etc. could and should be used to set other theme properties, such as size or font</blockquote>
<button class="btn">I'm a button</button>
<div class="thing">I'm a component that hasn't opted-in to any theme styles, so I'm unaffected.</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment