60
52

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ateam引越し侍Advent Calendar 2016

Day 14

sass-lintのエラールールを日本語で分かるようにした

Last updated at Posted at 2016-12-13

エイチーム引越し侍Advent Calendar 14日目の記事です。

今回は、CSS設計手法について書こう~と思っていたのですが、
最近sass-lintのエラールールについて調べる機会があり、日本語訳の一覧がほしいなーと思ったので日本語訳についてまとめようと思います。

こちら
https://github.com/sasstools/sass-lint/tree/develop/docs/rules
を参考にしています。
各ルール内に入れている例は上記リンク先のルールにある例を引用しています。
ただ全部の例をもってきていないので分からない場合はリンク先で確認してください。

また、ルールは現時点ではすべて網羅できていませんが、また随時更新していこうかなと思ってます!
最初に書いておきますが、まじで長いです。
右側のスクロールバーをちらっと見るとわかるかと思いますが、まじでながいです。

エラーチェックの時のルール

ルールのルールって何、って話にはなってしまうんですが、エンジニアではない私が「?」ってなった点が
「0」と「1」の違いです。
この記事を読んでいる方の中にはそんなん分かってる!と思う方もたくさんいるとは思いますが一応説明を書きますね。

設定時に、

rules:
  zero-unit: 1

このようなルールを書いた場合、「0」がエラーチェックしない、「1」がエラーチェックする、です。
つまり上は「zero-unit」のエラーをチェックするという記述です。
例えば、

margin-bottom: 0px;

と書かれていた場合、zero-unit(値の0に単位をつけない)を「1」にしていたらエラーをチェックするので、上は×になります。
「0」にしていた場合は、チェックをしないだけなので「0px」だろうが「0」だろうが通ってしまいます。
つまり単位をつけることをOKとするわけではなく、単純にチェックしないだけなんですね。

それでは内容について早速いきます。さくさくいきます。

Attribute Quotes

アトリビュート(属性セレクタ)のクォーテーションマークの有無
チェックする場合、デフォルトは「true」(クォーテーションマークを使用)


// true
span[lang="pt"] {
  color: green;
}

// false
span[lang=pt] {
  color: green;
}

BEM Depth

BEMで書くときに中に入れられるセレクタ数の指定

option

max-depth: 数字 (デフォルトは1)

最大1にしたとき下記は超えているのでエラーになります。

.block {
  &__element {
    &__subelement {
      // 2つ
    }
  }
}

.block__element__subelement__subelement-two {
  // 3つ
}

Border Zero

borderが0pxのときの記述チェック

option

convention: '0'/'none' (デフォルトは0)
「0」でかくか「none」でかくか

それぞれチェックが通る書き方はこちら

// 0の場合
.foo {
  border: 0;
}

// noneの場合
.foo {
  border: none;
}

Brace Style

セレクタの後ろにある「{}」のスタイルのチェック

option

  • style: '1tbs', 'stroustrup', 'allman' (デフォルトは1tbs)
  • allow-single-line: true/false (デフォルトはtrue)

style

'1tbs'または'stroustrup'のときOKで、'allman'のときNGな書き方

.foo {
  content: 'foo';
}

'allman'のときOKで、それ以外のときNG

.foo
{
  content: 'foo';
}

セレクタのあと、同じ行に「{」を書くか改行して書くかの違いですね。
では、'1tbs'と'stroustrup'の違いは?となるわけですが、

それぞれOK、NGの例はこちらになります。

'1tbs'のときOKで、それ以外のときNGな書き方

@if ($foo) {
  $bar: 'bar';
} @else {
  $bar: false;
}

'stroustrup'のときOKで、それ以外のときNGな書き方

@if ($foo) {
  $bar: 'bar';
}
@else {
  $bar: false;
}

ifを使ったときなど閉じ括弧のあと次に続く場合の改行の有無です。

allow-single-line

一行で書いてもOKか

下記はtrueの場合はOK、falseの場合はNGになります。

.foo { content: 'foo'; }

// styleの設定が'1tbs'の場合はOK、それ以外はNG
@if ($foo) { $bar: 'foo'; } @else { $bar: false; }

// styleの設定が'stroustrup'または'allman'の場合はOK、'1tbs'はNG
@if ($foo) { $bar: 'foo'; }
@else { $bar: false; }

class-name-format

クラス名のフォーマットがルールに沿ってるかチェックします

option

  • allow-leading-underscore: true/false (デフォルトはtrue)
  • convention: デフォルトはhyphenatedlowercase
  • convention-explanation: conventionの項目でルールが設定できなかったときにカスタムする
  • ignore: 無視するclass名を記述

allow-leading-underscore

アンダースコアの使用

convention

クラス名の付け方のルール

設定 内容
hyphenatedlowercase 小文字をハイフンでつなぐ
camelcase キャメルケース
pascalcase パスカルケース
snakecase スネークケース

などなど

オプションは色々と細かく設定できるみたいです。
例えば

  • allow-leading-underscore: true
  • convention: hyphenatedlowercase

で設定した場合、

OKな書き方

.hyphenated-lowercase {
  content: '';

  &._with-leading-underscore {
    content: '';
  }
}

NGな書き方

.HYPHENATED-UPPERCASE {
  content: '';
}

.camelCase {
  content: '';

  @extend .snake_case;
}

というような感じになります。

こちらに関しては参考元ページにて様々な例が掲載されているのでそちらをご確認ください。
https://github.com/sasstools/sass-lint/blob/develop/docs/rules/class-name-format.md
(バリエーションが多いので諦めましたすみません…)

clean-import-paths

importするときにファイル名や拡張子をつけるか

option

  • leading-underscore: true/false (デフォルトはfalse)
  • filename-extension: true/false (デフォルトはfalse)

leading-underscore

importするファイル名にアンダースコアをつけるか

// false
@import 'foo';
@import 'bar/foo';

// true
@import '_foo';
@import '_bar/foo';

filename-extension

importするファイルの拡張子をつけるか

// false
@import 'foo';
@import 'bar/foo';

// true
@import 'foo.scss';
@import 'bar/foo.scss';

declarations-before-nesting

親セレクタのスタイルをネストした子セレクタの前に書く

// これはOK
foo {
  content: 'baz';

  .bar {
    content: 'qux';
  }
}

// これはNG
.foo {
  .bar {
    content: 'qux';
  }

  content: 'baz';
}

empty-args

mixinを定義するときまたは呼び出すときに「()」をつけるかつけないか

option

include: true/false (デフォルトはfalse)

// falseのときOKでtrueのときはNG
@mixin bar {
  padding: 10px;
}

.bar {
  @include bar;
}

// trueのときOKでfalseのときはNG
@mixin foo() {
  padding: 10px;
}

.foo {
  @include foo();
}

empty-line-between-blocks

ネストした時に各セレクタの上の行を一行空けるか空けないか

##Options

  • include: true/false (デフォルトはtrue)
  • allow-single-line-rulesets: true/false (デフォルトはtrue)

include

// true
.foo {
  content: 'foo';

  .bar {
    content: 'bar';

    // Waldo
    &--baz {
      content: 'baz';
    }
  }
}

// false
.foo {
  content: 'foo';
  .bar {
    content: 'bar';
    // Waldo
    &--baz {
      content: 'baz';
    }
  }
}

allow-single-line-rulesets

一行で書いてOKかをチェックするので、こちらがtrueの場合は下記の書き方がOKになります。

.foo { content: 'foo'; }

extends-before-declarations

スタイルの記述の前にextendを書く

// OK
.foo {
  @extend %bar;
  content: 'baz';
}

// NG
.foo {
  content: 'baz';
  @extend %bar;
}

extends-before-mixins

mixinをよみこむ前にextendを書く

// OK
foo {
  @extend %bar;
  @include baz;
}

// NG
.foo {
  @include baz;
  @extend %bar;
}

final-newline

一番最後に一行いれる

option

include: true/false (デフォルトはtrue)

falseのとき

.foo {
  content: 'bar';
} // 最後の行

trueのとき

.foo {
  content: 'bar';
}
// 最後の行

↑一行あける

force-attribute-nesting

セレクタに属性セレクタを続けて記述する場合は入れ子にする

// NG
input[type='radio'] {
  color: red;
}

// OK
input {
  &[type='radio'] {
    color: red;
  }
}

force-element-nesting

セレクタの後ろに続く要素は入れ子にする

// NG
div p {
  content: '';
}

// OK
div {
  p {
    content: '';
  }
}

force-pseudo-nesting

疑似要素にスタイルを指定するときはネストさせる

// NG
p:nth-of-type(2) {
  margin: 0;
}

.parent {
  .child {
    p::first-line {
      color: #ff0000;
    }
  }
}

.parent {
  .child {
    .sub p::first-line {
      color: #ff0000;
    }
  }
}

// OK
p {
  &:nth-of-type(2) {
    margin: 0;
  }
}

.parent {
  .child {
    p {
      &::first-line {
        color: #ff0000;
      }
    }
  }
}

.parent {
  .child {
    .sub p {
      &::first-line {
        color: #ff0000;
      }
    }
  }
}

function-name-format

関数名のルールの設定

基本的に上ででてきたクラス名のルールと同じです。
ただ、オプションにignoreは無いので注意。

詳しい設定例はこちらにあります。(function-name-formatのルールが記載されたページ)
https://github.com/sasstools/sass-lint/blob/develop/docs/rules/function-name-format.md

hex-length

16進数で記述するときの長さを指定

option

style: short/long (デフォルトはshort)

// shortの場合これはOK、longの場合はNG
.baz {
  color: #fff;
}

// longの場合はOK、shortの場合はNG
.baz {
  color: #ffffff;
}

// こんなときは短縮できないのでstyleをどちらに設定していても通ります
.qux {
  color: #123456;
}

hex-notation

16進数を記述するときに大文字か小文字かのチェック

option

style: lowercase(小文字)/uppercase(大文字) (デフォルトはlowercase)

// 小文字に設定した場合にOK
.baz {
  color: #12a;
}

// 大文字に設定した場合にOK
.baz {
  color: #12A;
}

id-name-format

ID名のルール設定

クラス名のルール設定と同じです。

詳しい設定例はこちらにあります。(id-name-formatのルールが記載されたページ)
https://github.com/sasstools/sass-lint/blob/develop/docs/rules/id-name-format.md

indentation

インデントの指定。タブとスペースを混ぜないようにチェック

option

size: 数字 または 'tab' (デフォルトはスペース2つ)

// 2スペースを指定したときこれはOK
.foo {
  content: 'bar';

  .baz {
    content: 'qux';

    // Waldo
    &--waldo {
      content: 'alpha';
    }
  }
}

// NG
.foo {
content: 'bar';
   .baz {
  content: 'qux';
  // Waldo
      &--waldo {
        content: 'alpha';
      }
    }
}

leading-zero

値の数字の1の位が0のときの記述

option

include: true/false (デフォルトはfalse)

// falseのときはOK、trueのときはNG
.foo {
  font-size: .5em;
}

// trueのときはOK、falseのときはNG
.foo {
  font-size: 0.5em;
}

max-file-line-count

最大行数を指定する

option

length: 数字, (デフォルトは300)

// NG(左の数字は行数を例として記述)
  1| .test {
  2|   color: red
  3| }
=====
~ snip ~
=====
299| .bar {
300|   color: blue;
301| }

max-line-length

行の最大文字数を指定する

option

length: 数字, (デフォルトは80)

// NG
.really--long--class-name--that-unfortunately--isnt--very--succint--and-looks-stupid {
  color: red;
}

// ==============================================================================
//
// This comment is too long clearly, we should probably make sure we have a rule to
// determine when we breach this length
//
// ==============================================================================

mixin-name-format

mixinの名前のルール設定

クラス名のルール設定とほぼ同じです。
ignoreは無いので注意

詳しい設定例はこちらにあります。(mixin-name-formatのルールが記載されたページ)
https://github.com/sasstools/sass-lint/blob/develop/docs/rules/mixin-name-format.md

mixins-before-declarations

mixinはスタイルの指定より前に書く

option

exclude(除外設定)

exclude: ['breakpoint', 'mq']
と書くと、指定したmixinはチェックから除外されます

// OK
.foo {
  @include bar;
  content: 'baz';

  @include breakpoint(500px) { // チェックから除外されたmixin
    content: 'qux';
  }

  @include mq(500px) { // チェックから除外されたmixin
    content: 'qux';
  }
}

// NG
.foo {
  content: 'baz';
  @include baz;
}

nesting-depth

どれぐらいまでネストできるかを指定する

option

max-depth: 数字 (デフォルトは2)

// max-depthが2の場合
.foo {
  .baz {
    &:hover {
      // ここまで
    }
  }
}

.block {
  &__element {
    &--modifier {
      // ここまで
    }
  }
}

no-attribute-selectors

属性セレクタにスタイルを書かない

// NG
[lang=ja] {
  content: 'bar';
}

[href^="#"] {
  content: 'norf';
}

input[type="text"] {
    border:1px solid #000;
}

no-color-hex

16進数で色を書かない

// NG
.baz {
  color: #fff;
}

// OK
.baz {
  color: white;
}

no-color-keywords

キーワードで色をかかない

// NG
.baz {
  color: white;
}

// OK
.baz {
  color: #fff;
}

no-combinators

コンビネータ(子孫、隣接セレクタなど)を使用しない

この書き方はすべてNG

foo > .bar {
  content: 'foo';
}

.foo .bar {
  content: 'qux';
}

.foo {
  > .bar {
    content: 'foo';
  }
}

no-css-comments

コメントの書き方のチェック

/* */

のブロックコメントは禁止。
コメントを書く場合は「//」をつかう一行コメント、もしくはブロックコメントを使う場合は「/*! */」というように「!」を加える

OKなコメントたち

// This is a good comment

// =========
// This is a good comment
// =========

//////////////////
// This is a good comment
//////////////////

/*! This is a good bang comment */

/*!
  * This is a good bang comment
**/

NGなコメントたち

/* This comment will appear in your compiled css */

/*
 * Mulitline comments are bad
 */

no-debug

@debug の使用を許可しない

// NG
@debug 'foo';

no-disallowed-properties

指定した特定のプロパティを使用したときにエラーを出す

option

properties: 使用NGにするプロパティ名 (デフォルトは空)

z-indexを使用したときにエラーを出す

ymlファイルの記述

no-disallowed-properties:
  - 1
  -
    'properties':
      - 'z-index'

上の設定をした時、下記はエラーになる

.foo {
  z-index: 10;
}

no-duplicate-properties

同じブロック内でのプロパティの重複を許可しない

option

exclude: 除外したいプロパティ (デフォルトは空)

このエラーをチェックするにした場合、

.foo {
  margin: 0 0 15px;
  margin: 0;
}

これはだめです。

例えば除外設定に

no-duplicate-properties:
  - 1
  -
    exclude:
      - display

こんな感じで「display」プロパティを指定したとします。(flex効かないときの対応とかとか)
その場合は

.display-block {
  display: flex;
  display: inline-block;
  float: right;
}

この記述はOKです。
しかし

.display-block {
  display: flex;
  float: right;
  display: inline-block;
}

このように間に別のプロパティが入ってきて、許可したプロパティが続いていない場合は重複とみなされてNGです。

no-empty-rulesets

空のルールセットを許可しない

// NG
.foo {

}

no-extends

extendの使用を許可しない

// NG
.foo {
  @extend %bar;
  @extend .bar;
  @extend #bar;
}

no-ids

IDセレクタの使用を許可しない

// NG
#foo {
  content: 'bar';
}

no-important

!important の使用を許可しない

.foo {
  content: 'bar' !important;
}

no-invalid-hex

使えない(使用できない)16進数があるときはエラーをだす

エラーが出る場合

// 3文字または6文字でないとき
$invalid-long: #1234567;
$invalid-med: #1234;
$invalid-short: #12;

// 無効な文字
$invalid-character-map: (
  invalid-characters-upper-letters: #GHIJKL,
  invalid-characters-upper-letters-short: #GHI,
);

no-mergeable-selectors

重複セレクタは許可しない

option

whitelist: 重複を許可するセレクタ (デフォルトは空)

.foo {
  content: 'bar';
}

// 重複
.foo {
  color: red;
}

オプションに
whitelist: ['div p', 'div a']
こんな感じで追加した場合は

div p {
  color: red;
}

// 重複だけどエラーでないよ
div p {
  content: '';
}

div a {
  color: blue;
}

// 重複だけどエラーでないよ
div a {
  content: '';
}

no-misspelled-properties

存在しなかったり誤字をしていたりする不明なプロパティのチェック

option

extra-properties: 除外する綴りのプロパティ (デフォルトは空)

NG例

// スペルミス
.foo {
  borders: 0;
}

// 存在しないプロパティ
.bar {
  border-right-left: 0;
}

// スペルミス
.baz {
  -webkit-transit1on: width 2s;
}

オプションに 「transit1ion」 を追加すると

// incorrect spelling now whitelisted
.baz {
  -webkit-transit1on: width 2s;
}

// incorrect spelling now whitelisted
.quz {
  transit1on: width 2s;
}

これらはOKになります。

no-qualifying-elements

タグに属性・クラス・IDセレクタをつけてスタイルを指定しない

option

  • allow-element-with-attribute: true/false (デフォルトはfalse)
  • allow-element-with-class: true/false (デフォルトはfalse)
  • allow-element-with-id: true/false (デフォルトはfalse)

何も設定していないときはこれらはNG

div.foo {
  content: 'foo';
}

ul#foo {
  content: 'foo';
}

input[type='email'] {
  content: 'foo';
}

allow-element-with-attribute

属性セレクタを許可するか

trueにしたとき下記はOK、falseにしたときはエラーになります。

input[type='email'] {
  content: 'foo';
}

a[href] {
  content: 'foo';
}

allow-element-with-class

クラスセレクタを許可するか

trueにしたとき下記はOK、falseにしたときはエラーになります。

div.foo {
  content: 'foo';
}

allow-element-with-id

IDセレクタを許可するか

trueにしたとき下記はOK、falseにしたときはエラーになります。

ul#foo {
  content: 'foo';
}

no-trailing-whitespace

末尾のスペースを許可しない

// NG(\sはスペースかタブだと思ってください)
.foo {\s
  margin: 1.5rem;
}

.foo {
  margin: .5rem;\s
}

.foo {
  margin: .4rem;
}\s

no-trailing-zero

小数点以下の末尾の0は許可しない

// NG
.foo {
  margin: 1.500rem;
}

.foo {
  margin: 4.0rem;
}

no-transition-all

transitionプロパティ使用時に「all」の指定を許可しない

// NG
.foo {
  transition: all 2s;
}

.bar {
  transition-property: all 2s;
}

no-universal-selectors

ユニバーサルセレクタ「*」の使用を許可しない

// NG
* {
  content: 'foo';
}

*:before,
*:after {
  content: 'norf';
}

no-url-domains

URLを指定するときにドメインの記述を許可しない

// OK
.foo {
  background-image: url('/img/bar.png');
}

// NG
.foo {
  background-image: url('https://foo.com/img/bar.png');
}

no-url-protocols

URLを指定するときに、ドメインを含んだものと//から始まる絶対パスの指定を許可しない

option

allow-protocol-relative-urls: true/false (デフォルトはfalse)
※sass-lint 2.0では廃止予定らしいです。

falseの場合

// OK
.foo {
  background-image: url('/img/bar.png');
}

.foo {
  background-image: url('img/bar.png');
}

.foo {
  background-image: url('bar.png');
}

// NG
.foo {
  background-image: url('https://foo.com/img/bar.png');
}

.foo {
  background-image: url('http://foo.com/img/bar.png');
}

.foo {
  background-image: url('//foo.com/img/bar.png');
}

trueにした場合は

.foo {
  background-image: url('//foo.com/img/bar.png');
}

これがOKになります(引き続き相対パスはOK、ドメイン含むものはNG)

no-warn.md

@warn の使用を許可しない

// NG
@warn 'foo';

space-after-bang

「!」のあとのスペースの有無をチェック

option

include: true/false (デフォルトはfalse)

// false(空けない)のときはOK、trueのときはNG
.foo {
  content: 'bar' !important;
}

// true(空ける)のときはOK、falseのときはNG
.foo {
  content: 'bar' ! important;
}

space-after-colon

コロン(:)のあとのスペースの有無をチェック

option

include: true/false (デフォルトはtrue)

// true(空ける)のときはOK、falseのときはNG
.foo {
  content: 'bar';
}

// false(空けない)のときはOK、trueのときはNG
.foo {
  content:'bar';
}

space-after-comma

カンマ(,)のあとのスペースの有無をチェック

option

include: true/false (デフォルトはtrue)

// true(空ける)のときはOK、falseのときはNG
.foo {
  @include baz('foo', 'bar');

  box-shadow: 1px 1px black, 1px 1px black;
}

// false(空けない)のときはOK、trueのときはNG
.foo {
  @include baz('foo','bar');

  box-shadow: 1px 1px black,1px 1px black;
}

space-around-operator

演算子(+, -, /, *, %, <, > ==, !=, <=, >=)の前後のスペースをチェック

option

include: true/false (デフォルトはtrue)

// true(空ける)のときはOK、falseのときはNG
foo {
  margin: 5px + 15px;
}

$foo: 1 + 1;
$bar: 2 - 1;

@if $foo == $bar {
  $baz: 1;
}

// false(空けない)のときはOK、trueのときはNG
.foo {
  margin: 5px+15px;
}

$foo: 1+1;
$bar: 2-1;

@if $foo==$bar {
  $baz: 1;
}

trueとfalseともに、複数のスペースが含むものはエラーになります。

.foo {
  margin: 5px   +       15px;
}

$foo: 1      +1;
$bar: 2-     1;

space-before-bang

「!」の前のスペースの有無をチェック

option

include: true/false (デフォルトはtrue)

// true(空ける)のときはOK、falseのときはNG
.foo {
  content: 'bar' !important;
}

// false(空けない)のときはOK、trueのときはNG
.foo {
  content: 'bar'!important;
}

space-before-brace

「{」の前のスペースの有無

option

include: true/false (デフォルトはtrue)

// true(空ける)のときはOK、falseのときはNG
.foo {
  content: 'bar';
}

// false(空けない)のときはOK、trueのときはNG
.foo{
  content: 'bar';
}

space-before-colon

コロン「:」の前のスペースの有無

option

include: true/false (デフォルトはfalse)

// false(空けない)のときはOK、trueのときはNG
.foo {
  content: 'bar';
}

// true(空ける)のときはOK、falseのときはNG
.foo {
  content :'bar';
}

space-between-parens

「()」内の最初と最後のスペースの有無

option

include: true/false (デフォルトはfalse)

// false(空けない)のときはOK、trueのときはNG
@function foo($bar) {
  @return $bar;
}

// true(空ける)のときはOK、falseのときはNG
@function foo( $bar ) {
  @return $bar;
}

@mixin bar($baz ) {
  content: $baz;
}

.foo {
  @include bar( 'Hello' );
  content: foo( 'bar');
  width: calc( 100% - 10px);
}

trailing-semicolon

ブロックの最後のスタイルにつくセミコロン「;」の有無
(Sassだと除外可)

option

include: true/false (デフォルトはtrue)

// true(つける)のときはOK、falseのときはNG
.foo {
  content: 'bar';
  content: 'baz';

  .waldo {
    content: 'where';
  }
}

// false(つけない)のときはOK、trueのときはNG
.foo {
  content: 'bar';
  content: 'baz'

  .waldo {
    content: 'where'
  }
}

url-quotes

URLはクォーテーションマークで囲む

// OK
.foo {
  background-image: url('foo.png');
}

// NG
.bar {
  background-image: url(foo.png);
}

variable-name-format

変数名のルールの設定

基本的に上ででてきたクラス名のルール設定と同じです。
ただ、オプションにignoreは無いので注意。

詳しい設定例はこちらにあります。(variable-name-formatのルールが記載されたページ)
https://github.com/sasstools/sass-lint/blob/develop/docs/rules/variable-name-format.md

zero-unit

値が「0」のときに単位をつけるか

option

include: true/false (デフォルトはfalse)

// falseのときOKでtrueのときはNG
.foo {
  margin: 0;
}

.bar {
  padding: 5px 0 0;
}

// trueのときOKでfalseのときはNG
.foo {
  margin: 0px;
}

.bar {
  padding: 5px 0px 0px;
}

さいごに

おわりです。長かったですね。正直こんなに長くなると思ってなかったです。
後半になるにつれて書き方が雑になってしまいました…。
冒頭でも書きましたが、全部は書けていないので随時更新する予定ではいます。
sass-lint使用初心者のため、違うよーっていう部分がありましたらご指摘いただけるとうれしいです。

おつかれさまでした!:relaxed:

60
52
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
60
52

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?