【CSS】 忙しい人のためのSMACS
SMACSS (Scalable and Modular Architecture for CSS; スマックス) の概要についてご紹介致します。
SMACSS概要
CSSを5つにカテゴライズした構造
カテゴリ | 概要 |
---|---|
Base | Reset CSSなどのスタイルの基本 |
Layout | ページのセクション(header, footer, content) |
Module | 再利用可能なモジュールパーツ |
State | Layout, Moduleの特定状態下でのスタイル |
Theme | Layout, Module, Stateの見え方 *1 |
各カテゴリの詳細
対象セレクタ
カテゴリ | 対象セレクタ |
---|---|
Base | Element selector ( div , a )Descendant selector ( ul li )Child selector ( ul>li )Pseudo-classes( button:hover ) |
Layout | ID Class |
Module | Class |
State | Class |
Theme | Class |
記法
カテゴリ | 記法 |
---|---|
Base | - |
Layout | ID: 的確な命名 *2 、Class: プレフィックス l- |
Module | - |
State | Module名を含めることを推奨 |
Theme | プレフィックス theme- *3 |
例
カテゴリ | 例 |
---|---|
Base | div { margin: 0; } |
Layout | #header { margin: 0; } .l-footer { margin: 0; } |
Module | .module { font-size: 1rem; } .module-submodule { font-size: 1.2rem; } .module.module-submodule { font-size: 1.4rem; } |
State | .is-module-active { color: #f00; } |
Theme | .theme-module { color: #777; } |
!important
の利用可否
カテゴリ | ファイル |
---|---|
Base | 不可 |
Layout | 非推奨 |
Module | 非推奨 |
State | 使用可 |
Theme | 非推奨 |
SCSSの利用時
ファイル構成
カテゴリ | ファイル |
---|---|
Base | base.scss *4 |
Layout | layout/xxx.scss *5 |
Module | module/xxx.scss *6 |
State | states.scss *7 |
Theme | - |
+-layout/ | +-grid.scss | +-... +-module/ | +-callout.scss | +-btn.scss | +-... +-base.scss +-states.scss +-site-settings.scss +-mixins.scss
基本ファイルでの @import
内容
@import "site-settings", "mixins", "base", "states", "layout/grid", "module/callout";
まとめ
- SMACSS は CSS を5つにカテゴライズした構造
- Base, Layout, Module, State, Theme
最後に
忙しい人向けに、ざっくりとSMACSSについてまとめました。 細かい部分については注記を参照して下さい。
SMACSSは、2011年にJonathan Snookさんによって提唱されたArchitectureで、OOCSS (Object Oriented) や CSS Wrokflow などの影響を受けています。 また、BEMやFLOCSS、ECSSなどとの比較にも出されることが多く、おさえておいた方が良いアーキテクチャではあります。
公式ドキュメントには、カテゴリ毎の注意点やSCSSでの注意点、どうしてもBaseで避けられない事象についても記載があるため、 公式ドキュメントを読んでいただいた後に再度戻ってきていただくと、思い出せる手助けになると思います。
References
- 公式ドキュメント
- 公式ドキュメント (日本語版)
*1:Themeカテゴリはユーザーによって表示変更をする場合などに利用しますが、基本的には1つの表示形式になるため殆どの場合LayoutやModule、Stateで完結します。
*2:LayoutのプレフィックスをIDでも利用しても良いですが、ID利用が明確であるため必要性は薄いです。
*3:Themeのプレフィックスは複数の複雑な命名の場合のみ必要ですが、複雑でなければ不要です。
*4:SCSSでBaseを記述する場合、全て1つのファイルにする必要があります。
*5:SCSSでLayoutを記述する場合、1ファイルのみか、主なレイアウト毎にファイルを作成します。
*6:SCSSでModuleを記述する場合、各々のModuleでファイルを作成します。
*7:SCSSでStateを記述する場合、グローバルで利用するStateは専用のファイルに記述します。また、LayoutやModuleに関連するメディアクエリを含む場合は、Moduleのファイルに記述します。
【Rust】がばがばRust独学 - 13. Functional Language Features - 1 Closures (2)
Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)
今回も、Functional Language FeaturesのClosuresについて学びつつ、気になった点をコード確認した結果を記事にしています。
- versions
$ rustc --version rustc 1.40.0 (73528e339 2019-12-16)
Closures: Anonymous Functions that Can Capture Their Environment
Closures: Anonymous Functions that Can Capture Their Environment - The Rust Programming Language
Closure Type Inference and Annotation
下記のClosureでは、パラメータに対する型や戻り値に対する型を利用していません。
let expensive_closure = |num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num };
下記のように、型をつけることも可能です。
let expensive_closure = |num: u32| -> u32 { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num };
しかし、通常はClosure自体は短く、狭いコンテキスト内で使用され、かつ関数内のクローズドな匿名関数であるため、型を推測できます。
そのため、型をつける冗長性を無くすために、一般的に型を記述しません。明示性・明確性を高めたい場合は型をつけてよいと記載されていました。
Fn
について
昨日の記事にも出てきた Cacher
内に使用されている Fn
についてです。
struct Cacher<T> where T: Fn(u32) -> u32 { calculation: T, value: Option<u32>, }
クロージャーの型として Fn
には3つ利用することが出来ます。
FnOnce
: Closureが変数の所有権を取得FnMut
: 可変な値のOwnerを借用Fn
: 不変な値のOwnerを借用
ドキュメントを読んでも分からない・・・
ってことで、まずは定義を確認する。
pub trait Fn<Args> : FnMut<Args> { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call(&self, args: Args) -> Self::Output; } pub trait FnMut<Args> : FnOnce<Args> { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; } pub trait FnOnce<Args> { /// The returned type after the call operator is used. #[stable(feature = "fn_once_output", since = "1.12.0")] type Output; /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call_once(self, args: Args) -> Self::Output; }
違いとしては、下記になります。
FnOnce
:self
FnMut
:&mut self
Fn
:&self
上記から推測すると、 FnOnce
はClosureをそのまま利用するため1回利用・・・? Fn
はアドレス参照なので複数回利用?そして、 FnMut
はClosureを変更可能・・・?という風に読み取れますが、ちょっとわからないので、別記事に・・・
まとめ
- Closureには型定義を一般的には実施しない
- 明示性・明確性を高めたい場合は必要
FnOnce
、FnMut
、Fn
まじでわからん
最後に
Fn
系は今までに触ったことのない(もしくはよしなに頑張ってもらっていた?)概念なので正直分からない・・・
とりあえず、先に進めるために、次はIteratorsについて深めます。
【Rust】がばがばRust独学 - 13. Functional Language Features - 1 Closures (1)
Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)
今回は、Functional Language FeaturesのClosuresについて学びつつ、気になった点をコード確認した結果を記事にしています。
- versions
$ rustc --version rustc 1.40.0 (73528e339 2019-12-16)
- Functional Language Features: Iterators and Closures
- Closures: Anonymous Functions that Can Capture Their Environment
- まとめ
- 最後に
Functional Language Features: Iterators and Closures
Functional Language Features: Iterators and Closures - The Rust Programming Language
一般的な関数型言語の機能に似たRustの機能としては、下記をカバーしています。
- Closures : 変数に格納できる関数のような構造
- Iterators : 一連の要素を処理する方法
また、下記も記載されているそうです。
- Closures 、 Iterators を利用したI/Oの改善方法
- Closures 、 Iterators のパフォーマンス
Closures: Anonymous Functions that Can Capture Their Environment
Rustの Closures は下記が可能な匿名関数です。
- 変数への保存
- 引数として他関数へ渡せる
下記に、公式ドキュメント(Closures: Anonymous Functions that Can Capture Their Environment - The Rust Programming Language)のデモコードを元に、クロージャー利用までの手順を記載致します。
何も考慮せずに書いた場合
simulated_expensive_calculation
関数で約2秒停止するため、 generate_workout
で intensity < 25
の場合、約4秒の待機時間が発生します。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// 実行に約2秒かかる仮想関数 /// fn simulated_expensive_calculation(intensity: u32) -> u32 { println!("calculating slowly..."); let sec: u64 = 2; let duration: Duration = Duration::from_secs(sec); thread::sleep(duration); intensity } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { if intensity < 25 { println!( "Today, do {} pushups!", simulated_expensive_calculation(intensity) ); println!( "Next, do {} situps!", simulated_expensive_calculation(intensity) ); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", simulated_expensive_calculation(intensity) ); } } }
変数の利用
expensive_result
の変数に代入することにより、intensity < 25
の場合でも、約2秒の待機時間になります。
しかし、下記の場合、intensity >= 25 && random_number == 3
の場合であっても待機時間が発生してしまいます。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// 実行に約2秒かかる仮想関数 /// fn simulated_expensive_calculation(intensity: u32) -> u32 { println!("calculating slowly..."); let sec: u64 = 2; let duration: Duration = Duration::from_secs(sec); thread::sleep(duration); intensity } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { let expensive_result = simulated_expensive_calculation(intensity); if intensity < 25 { println!("Today, do {} pushups!", expensive_result); println!("Next, do {} situps!", expensive_result); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!("Today, run for {} minutes!", expensive_result); } } }
Closureの利用
無名関数を expensive_closure
に代入することで、Closureの宣言が可能です。
ただ、下記のままでは、通常で書いた内容と変わりがありません。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { // 実行に約2秒かかる仮想クロージャー let expensive_closure = |num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }; if intensity < 25 { println!("Today, do {} pushups!", expensive_closure(intensity)); println!("Next, do {} situps!", expensive_closure(intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!("Today, run for {} minutes!", expensive_closure(intensity)); } } }
Closure x Cacherの利用
struct Cacher<T>
としてClosureの結果をキャッシュすることで、無駄な処理がなく、かつ、最大約2秒の処理とすることが可能です。
use std::thread; use std::time::Duration; fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout(simulated_user_specified_value, simulated_random_number); } /// /// トレーニング計画を出力する関数 /// fn generate_workout(intensity: u32, random_number: u32) { // 実行に約2秒かかるキャッシュを利用した仮想クロージャー let mut expensive_result = Cacher::new(|num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }); if intensity < 25 { println!("Today, do {} pushups!", expensive_result.value(intensity)); println!("Next, do {} situps!", expensive_result.value(intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!( "Today, run for {} minutes!", expensive_result.value(intensity) ); } } } /// /// キャッシュロジック /// struct Cacher<T> where T: Fn(u32) -> u32, { calculation: T, value: Option<u32>, } /// /// キャッシュロジック impl /// impl<T> Cacher<T> where T: Fn(u32) -> u32, { fn new(calculation: T) -> Cacher<T> { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32 { match self.value { Some(v) => v, None => { let v = (self.calculation)(arg); self.value = Some(v); v } } } }
まとめ
let a = |b| { ... }
によってClosureを宣言可能
最後に
Closuresにはまだまだ細かく見るべき点が複数ありますが、一旦今回はここまでにします。
明日、Closures続きを記載します。