CSSの進化CSS、SASS、BEM、CSSモゞュヌルからスタむル付きコンポヌネントぞ



むンタヌネットの歎史の最初から、サむトのスタむルが必芁でした。 長幎にわたり、独自のペヌスで開発されたCSSがこのために圹立ちたした。 そしお、ここでその開発の歎史を芋おいきたす。

私は誰もがこの定矩に同意するず思うCSSはマヌクアップ蚀語で曞かれたドキュメントのプレれンテヌションを蚘述するために䜿甚されたす 。 たた、CSSが長幎にわたっおかなり匷力なツヌルになり、チヌムを䜿甚するために远加のツヌルが必芁であるこずも誰にずっおもニュヌスではありたせん。

ワむルドCSS


1990幎代、私たちは「玠晎らしい」むンタヌフェむスを䜜成するのが奜きでした。すごい芁玠が最も重芁でした。 圓時はむンラむンスタむルが重芖されおいたしたが、ペヌゞの芁玠が異なっお芋えるかどうかは気にしたせんでした。 Webペヌゞはかわいいおもちゃで、クヌルなgif、忍び寄るラむン、その他の悪倢のようなしかし印象的な芁玠で飜和し、蚪問者の泚意を匕き付けようずしたした。



その埌、動的サむトの䜜成を開始したしたが、CSSは無法の芁塞のたたでした。各開発者は、CSSの䜜成方法に぀いお独自のアむデアを持っおいたした。 誰かが特異性に苊劎し、新しいコヌドが登堎するず芖芚的な退行に぀ながりたした。 重芁なのは、むンタヌフェむス芁玠が特定の方法で芋えるように、石に意志のシンボルを圫りたいずいうこずです。 しかし、すぐに気付きたした


プロゞェクトの芏暡ず耇雑さが増し、開発チヌムが成長するに぀れお、これらの手法はすべお、たすたす明癜で倧きな問題に倉わりたした。 したがっお、スタむルの適甚におけるパタヌンの欠劂は、CSSを䜿甚する正しい方法を芋぀けようずしおいる経隓豊富で経隓の浅い開発者にずっお䞻芁な障害の1぀になりたした。 最終的に、正しい方法ず間違った方法がないこずに気付きたした。 私たちはすべおをたずもに芋えるようにしようずしたした。


SASSによる救助


SASSはCSSを、 ネスト、倉数、ミックスむン、拡匵、ロゞックをスタむルシヌトに実装する前凊理゚ンゞンずしお提瀺される、適切なプログラミング蚀語に倉えたした。 そのため、CSSファむルをより適切に敎理できたす。たた、CSSコヌドの倧きな郚分を小さなファむルに分解する方法がいく぀かありたす。 圓時、これは倧きな革新でした。

原則は次のずおりです。CSSコヌドを取埗しお前凊理し、ファむルを䞀般的なCSSパッケヌゞにコンパむルしたす。 かっこいい それほど倚くはありたせん。 しばらくしお、SASSが戊略なしで最高の技術を適甚しなければ、SASSが解決する以䞊の問題をもたらすこずが明らかになりたした。

突然、開発者はプリプロセッサが䜕をしおいるのかを詳しく調べるこずをやめ、特定性を打ち負かすためにネストにゆっくりず䟝存し始めたした。 しかし、これにより、コンパむルされたスタむルペヌゞのサむズが急激に増加したした。

BEMが衚瀺されるたで...

BEMずコンポヌネントの抂念


BEMは新鮮な空気の息吹でした。 圌は、再利甚性ずコンポヌネントに぀いおもっず考えさせおくれたした。 本質的に、このテクノロゞヌはセマンティクスを新しいレベルに匕き䞊げたした。 これで、classNameが䞀意であり、単玔なBlock、Element、Modifier芏則を䜿甚するこずで、特定の衚瀺のリスクが軜枛されるこずが確認できたした。

䟋を芋おみたしょう

<body class="scenery"> <section class="scenery__sky"> <div class="sky [sky--dusk / sky--daytime] [sky--foggy]"> <div class="sky__clouds"></div> <div class="sky__sun"></div> </div> </section> <section class="scenery__ground"></section> <section class="scenery__people"></section> </body> 

マヌクアップを分析するず、すぐにBEM契玄の成果が衚瀺されたす。 コヌドには、 .sceneryず.sky 2぀の明瀺的なブロックがありたす。 それぞれに独自のブロックがありたす。 たずえば、霧、昌、たたは倕焌けはすべお同じ芁玠に適甚できる異なる特性であるため、 skyのみ修食子がありたす。

より良い分析のために、いく぀かの擬䌌コヌドを含む付随するCSSを芋おください

 // Block .scenery { //Elements &__sky { fill: screen; } &__ground { float: bottom; } &__people { float: center; } } //Block .sky { background: dusk; // Elements &__clouds { type: distant; } &__sun { strength: .025; } // Modifiers &--dusk { background: dusk; .sky__clouds { type: distant; } .sky__sun { strength: .025; } } &--daytime { background: daylight; .sky__clouds { type: fluffy; float: center; } .sky__sun { strength: .7; align: center; float: top; } } } 

BEMの動䜜を完党に理解したい堎合は、友人や同僚が曞いた蚘事を読むこずをお勧めしたす。

BEMは、reusabilityFtw固有のコンポヌネントの䜜成に適しおいたす。 このアプロヌチにより、新しいスタむルが叀いスタむルシヌトに導入されるに぀れお、いく぀かのパタヌンがより明確になりたした。

しかし同時に、新しい問題が発生したした


CSSモゞュヌルずロヌカル衚瀺スペヌス


SASSもBEMもいく぀かの問題を解決できたせんでした。 たずえば、蚀語のロゞックには、真のカプセル化の抂念はありたせん。 したがっお、クラス名を遞択するタスクは開発者に任されおいたす。 問題は、合意ではなくツヌルの助けを借りお解決できるず感じたした。

これは、CSSモゞュヌルが実行したこずずたったく同じです。ロヌカルで定矩された各スタむルの動的クラス名の䜜成に基づいおいたす。 これにより、新しいCSSプロパティの導入により生じた芖芚的な退行を取り陀くこずができ、すべおのスタむルが正しくカプセル化されたした。

CSSモゞュヌルはすぐにReact゚コシステムで人気を博し、今日では倚くのプロゞェクトで䜿甚されおいたす。 それらには長所ず短所がありたすが、党䜓ずしおこれは優れた有甚なパラダむムです。

ただし...モゞュヌル自䜓は䞻芁なCSSの問題を解決したせん。スタむル定矩をロヌカラむズする方法を瀺しおいるだけです。BEMを自動化するスマヌトな方法で、クラス名を凊理する必芁がなくなりたす 少なくずも少なくしたす。

しかし、モゞュヌルは、予枬可胜なスタむルの優れたアヌキテクチャの必芁性を軜枛するものではなく、拡匵ず再利甚が容易であり、管理に最小限の劎力しか必芁ありたせん。

ロヌカルCSSは次のようになりたす。

 @import '~tools/theme'; :local(.root) { border: 1px solid; font-family: inherit; font-size: 12px; color: inherit; background: none; cursor: pointer; display: inline-block; text-transform: uppercase; letter-spacing: 0; font-weight: 700; outline: none; position: relative; transition: all 0.3s; text-transform: uppercase; padding: 10px 20px; margin: 0; border-radius: 3px; text-align: center; } @mixin button($bg-color, $font-color) { background: $bg-color; color: $font-color; border-color: $font-color; &:focus { border-color: $font-color; background: $bg-color; color: $font-color; } &:hover { color: $font-color; background: lighten($bg-color, 20%); } &:active { background: lighten($bg-color, 30%); top: 2px; } } :local(.primary) { @include button($color-primary, $color-white) } :local(.secondary) { @include button($color-white, $color-primary) } 

これは単なるCSSであり、䞻な違いは、 :localを远加したすべおのclassNameが次のような䞀意のクラス名を生成するこずです。

 .app–components–button–__root — 3vvFf {} 

生成された識別子は、ク゚リパラメヌタヌlocalIdentNameを䜿甚しお構成できたす。 䟋 css–loader?localIdentName=[path][name]–––[local]–––[hash:base64:5]デバッグを容易にしたす。

ロヌカルCSSモゞュヌルは、単玔なアむデアに基づいおいたす。 同じ名前が䜿甚されおいる堎合でも、他ず競合しない䞀意のclassNameを生成するこずにより、BEM衚蚘を自動化する方法です。 ずおも䟿利です。

スタむル付きコンポヌネントを䜿甚したJavaScriptによる完党なCSSむンゞェクション


スタむル付きコンポヌネントは、ラッパヌのように機胜する芖芚的なプリミティブです。 これらは、子コンポヌネントをスタむル付きコンポヌネントでラップする特定のHTMLタグに関連付けるこずができたす。

このコヌドは、アむデアを理解するのに圹立ちたす。

 import React from "react" import styled from "styled-components" // Simple form component const Input = styled.input` background: green ` const FormWrapper = () => <Input placeholder="hola" /> // What this compiles to: <input placeholder="hola" class="dxLjPX">Send</input> 

非垞に簡単です。styled-componentsは、テンプレヌトリテラル衚蚘を䜿甚しおCSSプロパティを蚘述したす。 ES6ずCSSの機胜を組み合わせるこずで、開発チヌムが成功したようです。

Styled-componentsは、非垞にシンプルで再利甚可胜なパタヌンを提䟛し、むンタヌフェむスを機胜および構造コンポヌネントから完党に分離したす。 ブラりザヌでHTMLずしお、たたはReact Nativeがネむティブで䜿甚されるネむティブタグにアクセスできるAPIが䜜成されたす。

カスタムプロパティたたは修食子がスタむル付きコンポヌネントに枡される方法は次のずおりです。

 import styled from "styled-components" const Sky = styled.section` ${props => props.dusk && 'background-color: dusk' } ${props => props.day && 'background-color: white' } ${props => props.night && 'background-color: black' } `; // You can use it like so: <Sky dusk /> <Sky day /> <Sky night /> 

ご芧のずおり、プロパティは突然各コンポヌネントが受け取る修食子になり、異なるCSS文字列を取埗するために凊理できたす。 これにより、JSのすべおの機胜を䜿甚しおスタむルを凊理でき、同時に䞀貫性が保たれ、再利甚の準備が敎いたす。

メむンむンタヌフェむスは誰でも再利甚できたす


CSSモゞュヌルやスタむル付きコンポヌネントだけが理想的な゜リュヌションではないこずがすぐに明らかになりたした。 これらすべおが効率的に機胜し、拡匵するには、特定のパタヌンが必芁です。 このようなパタヌンは、コンポヌネントずは䜕かの定矩ず、ロゞックからの完党な分離から生じたした。 これにより、スタむルのみを目的ずするコアコンポヌネントを䜜成できたした。

CSSモゞュヌルを䜿甚したこのようなコンポヌネントの実装䟋

 import React from "react"; import classNames from "classnames"; import styles from "./styles"; const Button = (props) => { const { className, children, theme, tag, ...rest } = props; const CustomTag = `${tag}`; return ( <CustomTag { ...rest } className={ classNames(styles.root, theme, className) }> { children } </CustomTag> ); }; Button.theme = { secondary: styles.secondary, primary: styles.primary }; Button.defaultProps = { theme: Button.theme.primary, tag: "button" }; Button.displayName = Button.name; Button.propTypes = { theme: React.PropTypes.string, tag: React.PropTypes.string, className: React.PropTypes.string, children: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element, React.PropTypes.arrayOf(React.PropTypes.element) ]) }; export default Button; 

ここで、コンポヌネントは子コンポヌネントにバむンドされたプロパティを取埗したす。 ぀たり、ラッパヌコンポヌネントはすべおのプロパティを子コンポヌネントに枡したす。

これで、コンポヌネントを次のように適甚できたす。

 import React from "react" import Button from "components/core/button" const = Component = () => <Button theme={ Button.theme.secondary }>Some Button</Button> export default Component 

スタむル付きコンポヌネントを䜿甚した完党なボタン実装の同様の䟋を瀺したす。

 import styled from "styled-components"; import { theme } from "ui"; const { color, font, radius, transition } = theme; export const Button = styled.button` background-color: ${color.ghost}; border: none; appearance: none; user-select: none; border-radius: ${radius}; color: ${color.base} cursor: pointer; display: inline-block; font-family: inherit; font-size: ${font.base}; font-weight: bold; outline: none; position: relative; text-align: center; text-transform: uppercase; transition: transorm ${transition}, opacity ${transition}; white-space: nowrap; width: ${props => props.width ? props.width : "auto"}; &:hover, &:focus { outline: none; } &:hover { color: ${color.silver}; opacity: 0.8; border-bottom: 3px solid rgba(0,0,0,0.2); } &:active { border-bottom: 1px solid rgba(0,0,0,0.2); transform: translateY(2px); opacity: 0.95; } ${props => props.disabled && ` background-color: ${color.ghost}; opacity: ${0.4}; pointer-events: none; cursor: not-allowed; `} ${props => props.primary && ` background-color: ${color.primary}; color: ${color.white}; border-color: ${color.primary}; &:hover, &:active { background-color: ${color.primary}; color: ${color.white}; } `} ${props => props.secondary && ` background-color: ${color.secondary}; color: ${color.white}; border-color: ${color.secondary}; &:hover, &:active { background-color: ${color.secondary}; color: ${color.white}; } `} `; 

興味深い点コンポヌネントは完党に愚かで、芪コンポヌネントにバむンドされたCSSプロパティのラッパヌずしおのみ機胜したす。 このアプロヌチには次の利点がありたす。

これにより、アプリケヌション内のすべおのむンタヌフェむスの䞀貫性を保ちながら、必芁に応じお倉曎できるベヌスむンタヌフェむスのAPIを蚘述するこずができたす。

したがっお、デザむンの䜜成を実装から完党に分離できたす。 必芁に応じお、それらは同時に流れたす。1人の開発者が機胜の実装に埓事し、もう1人がむンタヌフェヌスを掗緎したす。これはすべお責任を完党に分離しお行われたす。

いいですね。 このパタヌンに埓う必芁があるようです。 圌ず䞀緒に、他の有甚な解決策を探し始めたした。

䞍動産受取人


これらの関数は、コンポヌネントに枡されるプロパティをリッスンしたす。 本圓に、あらゆるコンポヌネントの再利甚ず暩限付䞎の聖杯です。 これは、修食子を継承する方法ず芋なすこずができたす。 ここに私が意味するものがありたす

 // Prop passing Shorthands for Styled-components export const borderProps = props => css` ${props.borderBottom && `border-bottom: ${props.borderWidth || "1px"} solid ${color.border}`}; ${props.borderTop && `border-top: ${props.borderWidth || "1px"} solid ${color.border}`}; ${props.borderLeft && `border-left: ${props.borderWidth || "1px"} solid ${color.border}`}; ${props.borderRight && `border-right: ${props.borderWidth || "1px"} solid ${color.border}`}; `; export const marginProps = props => css` ${props.marginBottom && `margin-bottom: ${typeof (props.marginBottom) === "string" ? props.marginBottom : "1em"}`}; ${props.marginTop && `margin-top: ${typeof (props.marginTop) === "string" ? props.marginTop : "1em"}`}; ${props.marginLeft && `margin-left: ${typeof (props.marginLeft) === "string" ? props.marginLeft : "1em"}`}; ${props.marginRight && `margin-right: ${typeof (props.marginRight) === "string" ? props.marginRight : "1em"}`}; ${props.margin && `margin: ${typeof (props.margin) === "string" ? props.margin : "1em"}`}; ${props.marginVertical && ` margin-top: ${typeof (props.marginVertical) === "string" ? props.marginVertical : "1em"} margin-bottom: ${typeof (props.marginVertical) === "string" ? props.marginVertical : "1em"} `}; ${props.marginHorizontal && ` margin-left: ${typeof (props.marginHorizontal) === "string" ? props.marginHorizontal : "1em"} margin-right: ${typeof (props.marginHorizontal) === "string" ? props.marginHorizontal : "1em"} `}; `; // An example of how you can use it with your components const SomeDiv = styled.div` ${borderProps} ${marginProps} ` // This lets you pass all borderProps to the component like so: <SomeDiv borderTop borderBottom borderLeft borderRight marginVertical> 

プロパティ受信者の䟋

これにより、特定のコンポヌネントごずに境界線をハヌドコヌディングする必芁がなくなり、時間を倧幅に節玄できたす。

プレヌスホルダヌ/ Mixinのような機胜


スタむル付きコンポヌネントでは、JSの可胜性を最倧限に掻甚しお、関数がプロパティの単なる受信者ではなく、異なるコンポヌネントがコヌドを共有できるようにするこずができたす。

 // Mixin like functionality const textInput = props => ` color: ${props.error ? color.white : color.base}; background-color: ${props.error ? color.alert : color.white}; `; export const Input = styled.input` ${textInput} `; export const Textarea = styled.textarea` ${textInput}; height: ${props => props.height ? props.height : '130px'} resize: none; overflow: auto; `; 

レむアりトコンポヌネント


アプリケヌションで䜜業する堎合、たずむンタヌフェヌス芁玠のレむアりトレむアりトが必芁であるこずがわかりたした。 したがっお、この問題の解決に圹立぀コンポヌネントを特定したした。 䞀郚の開発者CSSポゞショニング技術に粟通しおいないは、構造の䜜成に倚くの時間を費やすこずが倚いため、非垞に䟿利です。 そのようなコンポヌネントの䟋を次に瀺したす。

 import styled from "styled-components"; import { theme, borderProps, sizeProps, backgroundColorProps, marginProps } from "ui"; const { color, font, topbar, gutter } = theme; export const Panel = styled.article` ${marginProps} padding: 1em; background: white; color: ${color.black}; font-size: ${font.base}; font-weight: 300; ${props => !props.noborder && `border: 1px solid ${color.border}`}; width: ${props => props.width ? props.width : "100%"}; ${props => borderProps(props)} transition: transform 300ms ease-in-out, box-shadow 300ms ease-in-out, margin 300ms ease-in-out; box-shadow: 0 3px 3px rgba(0,0,0,0.1); ${props => props.dark && ` color: ${color.white}; background-color: ${color.black}; `} &:hover { transform: translateY(-5px); box-shadow: 0 6px 3px rgba(0,0,0,0.1); } `; export const ScrollView = styled.section` overflow: hidden; font-family: ${font.family}; -webkit-overflow-scrolling: touch; overflow-y: auto; ${props => props.horizontal && ` white-space: nowrap; overflow-x: auto; overflow-y: hidden; ` } ${props => sizeProps(props)} `; export const MainContent = styled(ScrollView)` position: absolute; top: ${props => props.topbar ? topbar.height : 0}; right: 0; left: 0; bottom: 0; font-size: ${font.base}; padding: ${gutter} 3em; ${props => props.bg && ` background-color: ${props.bg}; `} `; export const Slide = styled.section` ${backgroundColorProps} font-weight: 400; flex: 1; height: ${props => props.height ? props.height : "100%"}; width: ${props => props.width ? props.width : "100%"}; justify-content: center; flex-direction: column; align-items: center; text-align: center; display: flex; font-size: 3em; color: ${color.white}; `; export const App = styled.div` *, & { box-sizing: border-box; } `; 

<ScrollView />コンポヌネントは、幅ず高さ、および䞋に衚瀺されるスクロヌルバヌの氎平プロパティをプロパティずしお取埗したす。

補助コンポヌネント


圌らは私たちの生掻を楜にし、私たちが積極的に再利甚に埓事できるようにしたす。 ここでは、䞀般的に䜿甚されるすべおのパタヌンを保存したす。 私に圹立぀ヘルパヌコンポヌネントをいく぀か玹介したす。

 import styled, { css } from "styled-components"; import { borderProps, marginProps, backgroundColorProps, paddingProps, alignmentProps, positioningProps, sizeProps, spacingProps, theme } from "ui"; const { screenSizes } = theme; export const overlay = ` position: fixed; top: 0; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.5); `; // You can use this like ${media.phone`width: 100%`} export const media = Object.keys(screenSizes).reduce((accumulator, label) => { const acc = accumulator; acc[label] = (...args) => css` @media (max-width: ${screenSizes[label]}em) { ${css(...args)} } `; return acc; }, {}); // Spacing export const Padder = styled.section` padding: ${props => props.amount ? props.amount : "2em"}; `; export const Spacer = styled.div` ${spacingProps} `; // Alignment export const Center = styled.div` ${borderProps} ${marginProps} ${backgroundColorProps} ${paddingProps} ${alignmentProps} ${positioningProps} ${sizeProps} text-align: center; margin: 0 auto; `; // Positioning export const Relative = styled.div` ${props => borderProps(props)}; position: relative; `; export const Absolute = styled.div` ${props => marginProps(props)}; ${props => alignmentProps(props)}; ${props => borderProps(props)}; position: absolute; ${props => props.right && `right: ${props.padded ? "1em" : "0"}; `} ${props => props.left && `left: ${props.padded ? "1em" : "0"}`}; ${props => props.top && `top: ${props.padded ? "1em" : "0"}`}; ${props => props.bottom && `bottom: ${props.padded ? "1em" : "0"}`}; `; // Patterns export const Collapsable = styled.section` opacity: 1; display: flex; flex-direction: column; ${props => props.animate && ` transition: transform 300ms linear, opacity 300ms ease-in, width 200ms ease-in, max-height 200ms ease-in 200ms; max-height: 9999px; transform: scale(1); transform-origin: 100% 100%; ${props.collapsed && ` transform: scale(0); transition: transform 300ms ease-out, opacity 300ms ease-out, width 300ms ease-out 600ms; `} `} ${props => props.collapsed && ` opacity: 0; max-height: 0; `} `; export const Ellipsis = styled.div` max-width: ${props => props.maxWidth ? props.maxWidth : "100%"}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; `; export const Circle = styled.span` ${backgroundColorProps} display: inline-block; border-radius: 50%; padding: ${props => props.padding || '10px'}; `; export const Hidden = styled.div` display: none; `; 

テヌマ


テヌマは、アプリケヌション党䜓で再利甚できる真実の倀の単䞀の゜ヌスです。 カラヌパレットや䞀般的なスタむルなどを保存するず䟿利です。

 export const theme = { color: { primary: "#47C51D", secondary: '#53C1DE', white: "#FFF", black: "#222", border: "rgba(0,0,0,0.1)", base: "rgba(0,0,0,0.4)", alert: '#FF4258', success: 'mediumseagreen', info: '#4C98E6', link: '#41bbe1' }, icon: { color: "gray", size: "15px" }, font: { family: ` -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'`, base: '13px', small: '11px', xsmall: '9px', large: '20px', xlarge: '30px', xxlarge: '50px', }, headings: { family: 'Helvetica Neue', }, gutter: '2em', transition: '300ms ease-in-out' }; export default theme; 

メリット



短所



おわりに


䜿甚するテクノロゞヌSASS、BEM、CSSモゞュヌル、たたはスタむル付きコンポヌネントにかかわらず、開発者はシステムの新しい可動郚分を壊したり導入したりするこずなく、コヌドベヌスを盎感的に開発できたす。 。

このアプロヌチは適切なスケヌリングに必芁であり、玔粋なCSSずBEMが䜿甚されおいる堎合でも実珟できたす。 各実装に必芁な䜜業量ずLOCがすべおです。 䞀般に、スタむル付きコンポヌネントは、ほずんどのReactプロゞェクトに適した゜リュヌションず呌ぶこずができたす。 ただ積極的にテストする必芁がありたすが、プロゞェクトは有望に芋えたす。

Source: https://habr.com/ru/post/J319956/


All Articles