# SCSS
# Nomenclature
# Terminology
BEM, which stands for Block Element Modifier — core concepts of the methodology — is an effective and popular CSS naming convention. BEMIT is an enhancement to this methodology adding concepts from the Inverted Triangle CSS (ITCSS) organizational paradigm and constitutes a foundational component of the guidelines contained in this document. Please see this site for more complete information on BEM, this site for more complete information on BEMIT and ITCSS.
Hungarian Notation, used in the BEMIT methodology, is a naming convention which prefixes variables, often with single characters from a common stock, to indicate the kind or type of each, to help provide an indication of its intended purpose and relationship to its context. This helps to improve legibility of CSS classnames and patterns dramatically. Please see the Wikipedia article for more information.
The term pattern refers to any set of classes defined under a particular block, including the block itself, its elements and any compound selectors, such as those involving modifiers, :pseudo-classes, State Classes, etc.
A pattern consisting of only one class selector, i.e. its block, is considered to be lower-order; all other patterns are considered higher-order. For orders of Utilities, see §PatternTypes.Utilities.Orders.
Super- and sub-patterns refer to composed patterns, typically Components, which actively interact with each other.
# Classname Anatomy
.cn-x-class-name {...}
cn-
company name name-spacing prefixx-
Hungarian Notation (HN) type infixclass-name
.cn-x-block {...}
.cn-x-block__element {...}
.cn-x-block--modifier {...}
.cn-x-block--modifier .cn-x-block__element {...}
.cn-x-block.cn-is-state {...}
.cn-x-block.cn-is-state .cn-x-block__element {...}
.cn-x-block\@bp {...}
<tag-name class="cn-x-block@bp">
# Pattern Types
HN infix: o
, l
(layout), t
(transition)
Objects are recurring, often multi-class patterns.
An ideal architecture would consist entirely of Objects. Any refactors of the architecture ought to consider abstracting code into Objects while at the same time reducing their number (with attention paid to future flexibility); in practice, however, Objects are often rather simple constructs.
For more information, see §Composition.PatternTypes.Objects.
HN infix: c
Simply put, Components are parallel representations of Vue files. A Component's block is most often named essentially the same as the .vue
file.
Components should consist of only pattern-instance-specific styles, all other styles being relegated first to Objects, and then to Utilities. Static props should be replaced by Utilities, or Objects where possible.
For more information, see §Composition.PatternTypes.Components.
HN infix: u
, l
(layout)
.cn-u-pn-v {...}
pn-
abbreviated property name prefixv
abbreviated property value
Often consist of a single property with a common value.
A Utility pattern should consist of no more than one class (itself).
Higher-order Utilities should exist only when there is no discernable or meaningful pattern to be found, or if the combination of props is sufficiently simple and/or obvious.
In general, all Utility class properties have the !important
flag. This can often provide a distinction between higher-order Utilities and lower-order Objects.
For more information, see §Composition.PatternTypes.Utilities.
Whereas the order of other pattern types depends upon the number of class selectors present, the distinction between 'higher-' and 'lower-order' Utilities depends, by analogy, upon the number of properties present, with lower-order Utilities containing only one property, and higher- more than one.
Classifying Utilities in this way also hints at a soft but useful benchmark helping to delineate between Utilities and lower-order Objects.
# State Classes
HN infixes: is
, has
, etc.
State Classes are glorified, free-standing modifiers denoting differences in a block's state, such as being hovered, toggled, disabled, etc.
State Classes are generally programmatically applied.
Separating State Classes from the patterns they modify aims to make their usage more generic than regular modifiers. It allows them to be used across a pattern without breaking the proscription against directly modifying a block's elements, and between patterns without breaking the one against inter-pattern referencing.
In the case of naturally occurring states, such as hovered, disabled, etc., they provide an interface for applying, and framework for definitively grappling with, styles not to be bound to :pseudo-classes whose presence in practice may or may not actually conform to a DOM tree's intended presentational state, or for that matter even occur at all as in the case of complex, artificial input controls, etc.
Additionally, artificial states not based on any :pseudo-classes may be defined using the same nomenclature (see examples 3 and 4 below). As these states are not formally spec'd, effort should be made to find names as generic as possible to help maintain strict inter-pattern referencing boundaries.
cn-is-disabled
cn-is-hovered
cn-is-open
cn-has-content
For more information, see §Composition.StateClasses.
# Composition
# Terminology
Composition refers to the use of multiple classes on a single DOM element, or multiple patterns through a DOM tree, to the end of creating some synergetic effect that none would have accomplished on its own.
(For the listing of classes on a component instance, see §Vue.ComponentInstanceStructure.ClassAttributeOrder.)
# Rules
Selectors may not contain #id's.
Selectors may not contain tags (excluding base styles).
The !important
flag is reserved for Utilities and hacks.
A classname may have at most two levels: block and [sub-]element[s-set]. This is primarily to encourage the decoupling of styles and DOM.
Selectors must be as flat as possible, with the exception of :pseudo-classes and State Classes: an element's declaration should not need to be nested within, or a modifier's declaration appended to, its block to apply its properties, even if this would render its styles incomplete; elements and modifiers should not be used outside of the context of their block.
/* Valid */
.cn-x-block__element {...}
.cn-x-block--modifier {...}
/* Invalid */
.cn-x-block .cn-x-block__element {...}
.cn-x-block.cn-x-block--modifier {...}
Modifiers, including those for variants and themes, may be applied only to blocks, i.e. never to elements. For element modification, see §Nomenclature.BEMModifiedElements and §FileStructure.VariantAndThemeModifierClasses.
State Classes may directly modify elements; however, this should be avoided and the state description simplified to the block level wherever possible. (See §Nomenclature.ElementState)
Patterns referencing other patterns should be avoided at all costs. In most cases it should be possible to extend a pattern to provide itself interfaces for accessing subordinated patterns. One pattern should never need access to another's elements or modifiers. State Classes are an exception to this rule. For more information on composition with State Classes, see §StateClasses below.
If Vue component boundaries separate the patterns, the sub-component's API might define prop class lists for passing in interface elements; however, in most cases no API is even needed, as the block of Component patterns often sits at the root of the Vue component, allowing interface elements to be placed as static classes onto the Vue component instance.
<tag-name class="cn-x-super">
<tag-name class="cn-x-super__element cn-x-sub"></tag-name>
</tag-name>
.cn-x-super {
&__element {...} // cn-x-sub interface element
}
Selector combinators, such as .c1 > .c2
, etc., and relational :pseudo-classes, such as, :nth-child()
, etc., should select elements of blocks, not blocks themselves, to avoid unnecessarily connecting any two patterns.
// Valid
.cn-x-super {
&__first-element { // cn-x-sub interface element
&:first-child {...}
}
}
// Invalid
.cn-x-sub {
&:first-child {...}
}
Interface elements should be used to declare block-contextual styles. This helps to keep patterns recomposable.
# Pattern Types
In principle, Object declaration should not overlap, and Object composition should have as much overlap as possible.
Unlike Objects, Components are strictly forbidden from overlapping in composition; however, like Objects, Component declaration should also not overlap; anywhere Components overlap is an opportunity to relegate styles to an Object.
Utilities are meant to shoulder the burden of a Component pattern's Static props if and only if these props are, or are likely to be, used across multiple Components and no Object exists, or can be declared, to simplify their implementation across the architecture.
An Object's Static props may not be replaced by Utilities.
# State Classes
State Classes should not be assigned styles except in composition.
.cn-x-block.cn-is-state {...} /* Valid */
.cn-is-state {...} /* Invalid */
Monitoring sub-pattern state is possible by placing interface elements onto the sub-pattern wherever its state is defined and watching for the presence of State Classes through those elements. The resulting SCSS file structure would be modelled no differently from the one illustrated in §FileStructure.GeneralStructureAndFormatting.
<tag-name class="cn-x-super">
<tag-name class="cn-x-super__element cn-x-sub cn-is-state"></tag-name>
</tag-name>
.cn-x-super {
&__element { // cn-x-sub interface element
&.cn-is-state {...} // cn-x-sub interface element in cn-x-sub state
}
}
Note: Using interface elements to monitor sub-pattern state may seem unwieldy, and at odds with rules of Component composition, but, given that a pattern's state ought to be defined on its block, super- and sub-patterns only ever end up adjacent to one another, rather than mixed.
Composition of super-patterns with sub-pattern State Classes using interface elements follows essentially the same rule as for element states, with simplification of the state description (and styles) similarly aimed away from the (super-pattern) element and toward the (sub-pattern) block.
In contrast to state monitoring, in general super-patterns should not attempt to influence existing sub-pattern presentational states. Instead, State Classes may be applied programmatically and synchronously to all relevant points, or, if Vue component boundaries separate the patterns, through dynamic modifiers or prop class lists.
# Examples
<tag-name class="cn-x-block cn-is-state">
<tag-name class="cn-x-block__element"></tag-name>
</tag-name>
<tag-name class="cn-o-btn cn-o-btn--v-primary cn-o-btn--t-primary cn-is-toggled">
<tag-name class="cn-c-extended-btn__actions-list cn-o-semantic-list cn-l-p-a cn-l-r-0 cn-l-ta-sa">
<tag-name class="cn-c-switch__label cn-o-label cn-o-label--t-primary">
# File Structure
# Rules
There may be only one Component per file. Objects and Utilities may be grouped where it makes sense to do so.
All styles under a block are nested with the SASS &
. There is no increase in specificity.
// SCSS
.block {
&__element {
color: #000;
}
}
/* Compiled CSS */
.block__element { color: #000; }
All Config and Theme props should be defined in respective maps associated with the block, and should themselves be consolidated into the config and theme files.
State declarations should be ordered from lowest to highest in terms of priority to override others, with the disabled state generally being last.
Property Classes should be set within a selector in the following order, and marked (with the exception of the Static Class):
- Static
- Config
- Theme
- State
(For ordering collections of individual properties within classes, see §Properties.PropertyOrder.)
# General Structure and Formatting
//**
// [File and class(es) type]
//**
.cn-x-block {
// [Static and neutral State props]
// Config
// [Config props]
// Theme
// [Theme props]
// States [State props, state :pseudo-classs, state selectors]
&:state-pseudo-class {
// [all Prop Classes, same as above]
}
&.cn-is-state {
// [all Prop Classes, same as above]
}
&__element { // [elements]
// [all Prop Classes, same as above]
// States [element-specific state]
&:state-pseudo-class {
// [all Prop Classes, same as above]
}
&.cn-is-state {
// [all Prop Classes, same as above]
}
}
// States [block state :pseudo-classs, block state classes]
&:state-pseudo-class &__element {
// [all Prop Classes, same as above]
}
&.cn-is-state &__element {
// [all Prop Classes, same as above]
}
}
There should always be 2 whitespace lines between the first element and what comes before, even if what comes before is the block selector.
For Static, Config, Theme and State property classes, see §Properties.PropertyClasses.
# Variant and Theme modifier classes (extending to other configurations)
.cn-x-block {
// States
&:state-pseudo-class {...}
&.cn-is-state {...}
// Variants
&--v-variant-name {
// States
&:state-pseudo-class {...}
&.cn-is-state {...}
}
// Themes
&--t-theme-name {
// States
&:state-pseudo-class {...}
&.cn-is-state {...}
}
&__element {...}
&:state-pseudo-class &__element {...}
&.cn-is-state &__element {...}
// Variants
&--v-variant-name &__element {...}
&--v-variant-name:state-pseudo-class &__element {...}
&--v-variant-name.cn-is-state &__element {...}
// Themes
&--t-theme-name &__element {...}
&--t-theme-name:state-pseudo-class &__element {...}
&--t-theme-name.cn-is-state &__element {...}
}
# Properties
One property per line.
Prefer short-hand property names where possible.
White space between lines may be added to improve legibility (e.g. around a position
+ [offsets] + margin
group), but should span only one line.
Quoted property values should use only double-quotes "
.
Static props never change, no matter the config, theme or state. On Components they should be replaced by Utilities, or Objects where it makes sense to do so. For more information, see §Composition.PatternTypes.Utilities.
Static props often include abstract properties, such as overflow
, display
, etc; however, any prop is technically configurable. Whether a prop is configurable depends upon whether the pattern using it requires it to change at all across variants, themes or states; in practice, however, an unchanging prop is assumed to be configurable unless it has a generic value, such as margin: 0
, or top: 100%
; but, ultimately, whether a value is generic must be determined on a case-by-case basis.
Config props are typically any spatial properties configurable across a pattern's (potential) variants.
Config props often include margin
, padding
, font-size
, etc.
Theme props are typically any visual properties, configurable across a pattern's (potential) themes.
Unlike Config props, something considered a Theme prop is always a Theme prop, with rare exception, regardless of whether the pattern would otherwise configure it. Spatial prop inheritance is generally flat; whereas visual prop definitions derive through chains of successively more lower-level theme maps, to allow for more nuanced and powerful control over the theme's expression in the UI. In short, visual props tend toward being universal, and propagate overtop the more often instance-specific spatial props.
Theme props include border-radius
, color
, etc.
State props are any non-Config, non-Theme props configurable across a pattern's states.
State props are mostly a formal categorization, crossing Static props with an analogy to Config/Theme props.
These props may or may not be implemented in a selector statically, but, if not, their configuration, by definition, will come from somewhere beyond the pattern's own config or theme maps. Consider the following map:
$cursor: (
disabled: not-allowed,
);
State props often include cursor
, top
, etc.
The headings Abstract, Spatial, Visual and State are to help illustrate a natural scheme to order properties by.
The list of properties included is certainly not exhaustive, but it should suffice to direct any future additions.
This order is to be applied per implemented Property Class. For implementing Property Classes, see §FileStructure.Rules, p.5 and §FileStructure.GeneralStructureAndFormatting.
-
cursor
user-select
pointer-events
overflow
display
- etc.
z-index
position
- [offsets]
top
bottom
left
right
transform
margin
margin-top
margin-bottom
margin-left
margin-right
-
width
height
padding
padding-top
padding-bottom
padding-left
padding-right
font-size
- etc.
line-height
border-radius
border
border-width
border-style
border-color
background-color
-
font-weight
font-style
text-decoration
- etc.
color
-
transtion
/transition-property
transition-duration
transition-timing-function
- etc.
# Token Maps
# Declaration
Token maps use the SASS map syntax:
$map: (
key-1: val,
key-2: (
key-1: val,
),
);
# Invoking Tokens
Tokens are invoked through token($map [, $token-chain])
.
A $token-chain
consists of a list of strings corresponding to a continuous or discontinuous, well ordered sequence of keys descending levels of the $map
parameter and pointing to some value the map may or may not hold.
The parameter is optional, or the sequence may be discontinuous, given the map make appropriate use of the default
keyword.
background-color: token($theme-main);
color: token($theme-btn, hover type);
The default
keyword may be used in a map declaration, at any level, to allow token()
invokations access to values without them having specified a key for that level in the map. It allows simplicity in map declaration, and ease of maintainence of both inheriting maps and implementing patterns, as it allows for invokations to make assumptions about a map's implementation details.
There may be only one default
keyword per set of keys, which should be placed before any named keys.
// SCSS
$theme-btn: (
default: (
fill: #00f,
),
hover: (
fill: #0f0,
),
);
.cn-x-btn {
background-color: token($theme-btn, fill);
}
/* Compiled CSS */
.cn-x-btn { background-color: #00f; }
token()
invokations can progressively delete leading $token-chain
links if no token is found at the end of the chain.
In combination with a map's use of the default
keyword, assimilation allows a pattern to anticipate its own needs, rather than having to wait to implement selectors until the design, or design tokens, have been fully implemented.
// SCSS
// map with 2/3 states implemented
$theme-btn: (
default: (
fill: #00f,
),
hover: (
fill: #0f0,
),
);
// 3/3 implementing selectors
.cn-x-btn {
background-color: token($theme-btn, fill);
&:hover {
background-color: token($theme-btn, hover fill);
}
&:active {
background-color: token($theme-bth, active fill);
}
}
/* Compiled CSS */
.cn-x-btn { background-color: #00f; }
.cn-x-btn:hover { background-color: #7f7fff; }
.cn-x-btn:active { background-color: #00f; } // gracefully fails to value at 'ø fill'
In the preceding example, active fill
is effectively equivalent to fill
.
If a $token-chain
is thought to require assimilation not from its start but from some arbitrary point in the middle, the map has likely become too big and/or its tree does not organize properties correctly and should be refactored. Consider the following malformed, and corrected, examples:
Malformed Assimilation example
// SCSS
$theme-btn: (
default: (
default: (
fill: #00f,
),
hover: (
fill: magenta,
),
),
secondary: (
default: (
fill: #0f0,
),
),
);
.cn-x-btn {
&--t-primary {
background-color: token($theme-btn, fill);
&:hover {
background-color: token($theme-btn, hover fill);
}
}
&--t-secondary {
background-color: token($theme-btn, secondary fill);
&:hover {
background-color: token($theme-btn, secondary hover fill);
}
}
}
/* Compiled CSS */
.cn-x-btn--t-primary { background-color: #00f; }
.cn-x-btn--t-primary:hover { background-color: magenta; }
.cn-x-btn--t-secondary { background-color: #0f0; }
.cn-x-btn--t-secondary:hover { background-color: magenta; } // Uses primary btn token
Corrected Assimilation example
// SCSS
$theme-btn-primary: (
default: (
fill: #00f,
),
hover: (
fill: magenta,
),
);
$theme-btn-secondary: (
default: (
fill: #0f0,
),
);
.cn-x-btn {
&--t-primary {
background-color: token($theme-btn-primary, fill);
&:hover {
background-color: token($theme-btn-primary, hover fill);
}
}
&--t-secondary {
background-color: token($theme-btn-secondary, secondary fill);
&:hover {
background-color: token($theme-btn-secondary, secondary hover fill);
}
}
}
/* Compiled CSS */
.cn-x-btn--t-primary { background-color: #00f; }
.cn-x-btn--t-primary:hover { background-color: magenta; }
.cn-x-btn--t-secondary { background-color: #0f0; }
.cn-x-btn--t-secondary:hover { background-color: #0f0; } // Uses own fallback
# Inheritance
Maps should be made to use other maps as a tree branches, with higher-level maps (higher with respect to configuration specificity - lower with respect to being the interface between tokens and implementing patterns) referencing lower-levels where it makes sense to do so.
Inheritance is not arbitrary but should be an explicit representation of underlying patterns in the design of the UI.
For a note contrasting the typical character of spatial and of visual prop inheritance, see §FileStructure.Properties.PropertyClasses.Theme, p.2.
// SCSS
$color: (
blue: (
default: #00f,
50: lighten(#00f, 0.5),
),
green: (
default: #0f0,
50: lighten(#0f0, 0.5),
),
);
$theme-main: (
default: (
default: token($color, blue),
highlight: token($color, blue 50),
),
secondary: (
default: token($color, green),
highlight: token($color, green 50),
),
);
$theme-btn: (
default: (
fill: token($theme-main),
),
hover: (
fill: token($theme-main, highlight),
),
);
//...
.cn-x-btn {
background-color: token($theme-btn, fill);
&:hover {
background-color: token($theme-btn, hover fill);
}
}
/* Compiled CSS */
.cn-x-btn { background-color: #00f; }
.cn-x-btn:hover { background-color: #7f7fff; }
# Note about the $color
map
The $color
map is the most widely inherited map and most often the ultimate root. It does not aim to make sense of the colours it holds but merely indexes them for use by other maps, leaving semantic concerns to any descendants.
Only in rare and exceptional cases is this map ever accessed by a higher-level map directly, and this should be avoided at all costs, and inheritance via lower-level maps leveraged instead.
# File Structure
Theme maps should be prefixed with the word "theme": $theme-map
.
Maps should have trailing commas (the last key-value pair is ended with a comma).
Maps should be organized within sections reflecting the directory structure, including Principals, Objects, Layout, Components and Templates.
Sections should be separated by 3 lines of whitespace.
All maps and their section header should be separated by 1 line of whitespace.
Within each section, loosely related, or unrelated, maps should be given extra whitespace (3 spaces to the regular 1).
//**
// [File and map type]
//**
//**
// Principals
//**
$first-map: (
default: (
default: val,
key: val,
),
key: val,
);
$second-map: (
default: val,
);
$third-unrelated-map: (
default: val,
);
//**
// Objects
//**
//...
Maps should be ordered from low-level (abstract) to high-level (concrete), and should generally end up reflecting the order of the directory main.scss
import order. Assuming inheritance is well implemented, any deviating order would fail to compile.
# Directory Structure
# Organization
- [root]/
|- main.scss
|- principals/
| `- _mixins.scss
|- objects/
|- components/
| `- component-subdirectory
|- templates/
|- supplemental/
`- utilities/
All files are imported into main.scss
. See §ImportOrder below.
All files except the main.scss
should be prefixed with an underscore.
Filenames should consist of hyphenated lowercase words.
The components folder should mirror that of the Vue directory structure (not pictured) where applicable.
# Import Order
// resets and third-party resources, incl. external fonts
// Principals
@import "principals/mixins";
@import "principals/theme";
@import "principals/config";
@import "principals/transitions";
@import "principals/base-styles";
// Objects
@import ...
// Layout
@import ...
// Components
@import ...
// Templates
@import ...
// Supplemental
@import "supplemental/print";
@import "supplemental/hacks";
// Utilities
@import ...
When importing, do not include any underscores _
or file extensions (e.g. .scss
, .css
, etc.).
For Objects, Components and Utilities, see §Nomenclature.PatternTypes.
For Layout and Templates, see §Notes below.
Layout contains any Objects designed for organizing other patterns on the page.
Templates contains any page-level Components.
Utilities are added in reverse order; whereas the rest of the file is ordered from low to high specificity, utilities must be ordered high to low. This may seem counter-intuitive, but does make sense as general utilities should have a higher priority over specific ones. If in some apparently problematic use-case this seems not to be the case, the use-case ought to be reconsidered.
# Notes on specific files
The base styles file is intended to include any tag-specific resets or initializations. Consider the following:
* {
box-sizing: border-box;
}
body {
font-family: token($theme-font);
}
i {
font-style: italic;
}
img,
video {
max-width: 100%;
}
These files consolidate all token maps. For the organization of maps within these files, see §TokenMaps.FileStructure.
Hacks are last-minute additions which are not taken into account by the rest of the architecture and should be refactored with due consideration at the first opportunity. It is important to separate these selectors to avoid losing, first, their individual occurrences and, ultimately, at the extreme, control over the architecture.
# Vue
# Component Instance Structure and Formatting
# Rules
Tags and attributes should conform to the preexisting HTML naming convention of hyphenated lowercase words.
<!-- Valid -->
<custom-component
:custom-attribute="value"
...
<!-- Invalid -->
<CustomComponent
:customAttribute="value"
...
Attributes should be listed along the length of the file, with each attribute on its own line, and a line of white space separating each attribute class.
Listing attributes (illustrated below) applies unless the attribute list is sufficiently small as to warrant no white-space separation between attribute groups. In some cases an attribute list may be so small as even to warrant no line breaks; however in such cases the list consists typically of no more than about 3 attributes. It always applies, however, when a tag's class list has multiple class groups.
Additional whitespace may be used sparingly to punctuate large or diverse groups of attributes.
Directives should be placed specially on the same line as the tag name.
<tag-name v-if="condition"
...
For <transition>
tags, the name attribute may be placed similarly to directives.
<transition name="name"
...
All attribute values following the =
sign should be surrounded by "
quotes.
Custom boolean-based attributes should be named declaratively.
<!-- Valid -->
<tag-name no-header></tag-name>
<!-- Invalid -->
<tag-name has-no-header></tag-name>
Attributes whose values are strings should not use v-bind:
or its shorthand :
with the value quoted as a string.
The closing bracket of the opening tag should be placed on the line following the attribute list, and at the same indentation depth as the tag name itself, rather than immediately after the last attribute or at some other indentation. This is critical for at-a-glance legibility, and provides text-editors the option to collapse attribute lists and contained content independently of each other. See the following malformed, and corrected, examples:
Malformed formatting example
<div
attribute="value">
...
</div>
Corrected formatting example
<div
attribute="value"
>
...
</div>
Tags with only textual content should open and close on the same line.
<span
attribute="value"
>Textual Content</span>
# Attribute Order
Attributes, like properties, are ordered according to class, but in some cases supporting properties are grouped as with v-model
; whereas properties are organized from abstract to concrete to aesthetic, attributes are organized analogously, by functionality, from crucial to noncrucial, by which class
is categorized as technically functional but exceptionally noncrucial.
-
v-for
key
v-if
v-show
- etc.
v-model
value
@change
@input
disabled
aria-disabled
- etc.
- any custom active events
click
-
mousedown
mouseover
mouseout
- etc.
hover
-
keydown
keyup
- etc.
- etc.
- any custom passive events
focus
blur
- etc.
Any props essential for the component to function, e.g. a table row's :row
data.
id
for
name
type
- any attributes setting constraints upon an input, e.g.
min
,rows
, etc. required
autocomplete
placeholder
- etc.
tabindex
role
title
alt
aria-hidden
-
aria-labelledby
aria-describedby
- etc.
-
aria-haspopup
aria-owns
aria-expanded
aria-activedescendant
- etc.
- etc.
- any dynamic modifier props
- any sub-component prop class list
- etc.
class
<tag-name v-for="(item, index) in items" :key="index" v-if="condition"
:is="tag"
ref="ref"
v-model="model"
@change="onChange"
:disabled="disabled"
...
@click="onClick"
@keydown.enter="onEnter"
...
@focus="onFocus"
@blur="onBlur"
...
:component-prop="componentProp"
...
:id="id"
:type="type"
:autocomplete="autocomplete"
:placeholder="placeholder"
...
:tabindex="tabindex"
:aria-labelledby="label"
aria-haspopup="true"
...
:variant="variant"
:component-class-list="componentClassList"
...
:class="['static class list',
componentPropClassList,
{ 'conditional class list': classCondition }]"
>
...
# Class Attribute
- Static Classes
- Dynamic Modifiers
- Prop Class Lists
- Conditional Class Lists
Static classes are always present on a tag.
Dynamic modifiers are classes constructed in part from values passed into the component as props, such as might be used for accessing a pattern's variants, themes or states.
They are best treated the same as Prop Class Lists after being stored in variables in the component's data()
or computed
objects.
Prop class lists are any class lists passed into the component as props and used as is.
Conditional class lists should be ordered sensibly, from most to least crucial in its effect on a component's presentation; however, this must be determined on a component-by-component basis.
For legibility, State Classes should come last, and be ordered similarly to the note in §SCSS.FileStructure.Rules, p.4 on SCSS state declarations, but for organizational, rather than functional, purposes.
When using more than one class group, the class attribute should be moved to its own line if it hasn't already, and subsequent groups should indent to the same level as within the opening quotation mark (see the illustration below).
In general, within each class group, classes should be ordered by class type, and, within each type, from highest implementation specificity to lowest:
- Components
- Objects
- Higher-order
- Lower-order
- Utilities
- Higher-order
- Lower-order
(For the distinction between higher- and lower-order patterns, see §Nomenclature.Terminology, p.4.)
Modifiers should be listed immediately after the pattern's block class.
An extra space may be added between each class type if the number of classes belonging to any type is greater than 1.
<tag-name
:class="['cn-c-component cn-o-object cn-o-object--modifier cn-u-p-v cn-u-p2-v',
variantClassList,
componentPropClassList,
{ 'cn-is-open': isOpenCondition },
{ 'cn-is-disabled': isDisabledCondition }]"
>
...