spiracss/keyframes-naming
Validates placement and naming of @keyframes.
Purpose
- Clarify animation responsibilities and placement
- Avoid naming collisions and improve discoverability
What it checks
@keyframesmust be at the root only (not inside@media/@layer)@keyframesmust be grouped at the end of the file- Names are
{block}-{action}or{block}-{element}-{action} - Block/Element casing follows
stylelint.base.naming - If the token after {block} matches an element name in the file, it is treated as {element}; otherwise the full suffix is treated as {action}
- Action is 1-3 words in the
blockCasecase (adjustable viaactionMaxWords)
OK
.sample-block { opacity: 0;}
@keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; }}NG
@media (min-width: 768px) { @keyframes sample-block-fade-in { // NG: not at the root from { opacity: 0; } to { opacity: 1; } }}@keyframes sample-block-fade-in-out-fast { // NG: action exceeds max words (default: 3) from { opacity: 0; } to { opacity: 1; }}Why
- Grouping at the root and at the end makes animations easier to find/manage
- Tying names to Block/Element avoids collisions
Exceptions / notes
- For shared animations, use a prefix like
kf-and consolidate them inkeyframes.scss - If the root Block cannot be resolved, it warns and skips naming checks (configurable via
blockSource/blockWarnMissing)
Error list
needRoot (@keyframes must be at the root)
Example:
// NG@media (min-width: 768px) { @keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; } }}
// OK@keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; }}Reason: group animation definitions for easier discovery
needTail (must be placed at the end of the file)
Example:
// NG@keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; }}
.sample-block { opacity: 0;}
// OK.sample-block { opacity: 0;}
@keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; }}Reason: make animation definitions easy to find
invalidName (invalid naming)
Example:
// NG@keyframes fade-in { from { opacity: 0; } to { opacity: 1; }}
// OK@keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; }}Reason: tie names to Block/Element to avoid collisions
invalidSharedName (invalid shared animation name)
Example:
// NG@keyframes fade-in { from { opacity: 0; } to { opacity: 1; }}
// OK@keyframes kf-fade-in { from { opacity: 0; } to { opacity: 1; }}Reason: make shared animations explicit
sharedFileOnly (shared animation in the wrong file)
Example:
// NG// in card-list.scss@keyframes kf-fade-in { from { opacity: 0; } to { opacity: 1; }}
// OK// in keyframes.scss@keyframes kf-fade-in { from { opacity: 0; } to { opacity: 1; }}Reason: fix the location for shared definitions
missingBlock (cannot resolve root Block)
Example:
// NG@keyframes fade-in { from { opacity: 0; } to { opacity: 1; }}
// OK.sample-block { opacity: 0;}@keyframes sample-block-fade-in { from { opacity: 0; } to { opacity: 1; }}Reason: cannot bind the name to a Block
selectorParseFailed (selector parse failed)
Example:
// NG.sample-block { > : { color: #111; }}
// OK.sample-block { > .title { color: #111; }}Reason: unparseable selectors cannot be validated