Pleasanterのカスタマイズで使えるオススメのCSS10選

## はじめに
ここ数年のCSSはどんどん進化しており2024年だけで50個以上の機能が追加されました。
以前だとJavaScriptで実装するしかなかった機能が数行のCSSだけで表現できたり、SASSなどを使わないとできなかったことが可能になったりと、年々便利になっています。
しかし進化するスピードが速すぎてなかなか最新の情報をキャッチアップできていない、何となく聞いたことはあるけど、どう使うのかわからないといった機能も多いのではないでしょうか。
今回はPleasanterのカスタマイズで使えそうなスタイルを10個集めてみました。
以前から存在していたけれどあまり使ったことのない機能や、ここ最近で全ブラウザ対応した最新のプロパティなど、オススメのスタイルばかりです！


# 1. CSSネスティング

```scss
.unit {
  font-size: 12px;
}
.unit .text {
  color: #000;
}
.unit .link {
  color: #00f;
}
.unit .link:hover {
  color: #0ff;
}

/* ↑のスタイルが↓のように書ける */

.unit {
  font-size: 12px;
  .text {
    color: #000;
  }
  .link {
    color: #00f;
    /* 
      & は入れ子セレクターで自分自身を返します
      ここでは .link の要素が該当します。
    */
    &:hover {
      color: #0ff;
    }
  }
}
```
まず最初に紹介したいのはCSSを入れ子で書けるネスト記法です。SASSなどで馴染みのある方も多いですよね。
入れ子にするメリットとしては　

- 親の中に閉じ込めることができるので他の要素に影響を与えにくい
- クラス名などが冗長になるのを防ぐ
- 可読性が上がりメンテナンスしやすい

などがあります。
特に新しいプロパティなどを覚える必要がなく実装も簡単。一度書いてみるとこの記法の便利さに気付くはず。
もちろん`テーブル管理`の`スタイル`でも使えるので是非お試しください！


# 2. :has()
```scss
.item:has(img) {
  color: #f00;
}
```
続きまして、2024年のCSSの現状調査をまとめた「[State of CSS 2024](https://2024.stateofcss.com/ja-JP/)」にて[もっとも使われた新機能](https://2024.stateofcss.com/en-US/features/)の第2位に選ばたれた、`has` 疑似クラスです。
私自身も今年はかなり使った印象です。
機能としては、`:has()`が設定されてる要素を親とし、引数に該当する要素が存在していれば、定義されたスタイルを適用します。
上のサンプルコードだと、「`.item`の中に`img`要素が存在していれば文字を赤くする」というコードになります。
つまり、こういうことですね。
```html
<div class="item">
  <img src="dummy.png" alt="">
  ここの文字は赤くなる
</div>
<div class="item">
  ここの文字は赤くならない
</div>
```
Pleasanterで使う場合、例えば指定レコードの編集画面にのみスタイルを適用することができます。
```scss
/* 
  bodyからみて
  <input id="Id" name="Id" type="hidden" value="12345678">のvalueを参照
*/
body:has(#Id[value="12345678"]) {
  /* レコードIDが12345678の場合に#Issues_Bodyの文字サイズを変更 */
  #Issues_Body {
    span {
      font-size: 15px;
    }
  }
}
```
引数にはカンマ区切りで複数の条件を指定したり、
```SCSS
div:has( ~ ul > li a + span) {
  /* ... */
}
```
のような複雑なセレクターも指定できます。

# 3. :not()
`:not()`は否定疑似クラスです。少し前に実装された疑似クラスで、引数に指定した条件以外の要素にスタイルを適用します。

こちらの例では`.item`に `.example`というクラスが同時に設定されていない場合に、フォントサイズを変更する処理が書かれています。
```scss
.item:not(.example) {
  font-size: 15px;
}
```

この疑似クラスが登場するまでは否定用のclass名を用意し、場合によってはjsでon/offが必要なケースもあったのですが、`:not()`が登場してからはCSSとHTMLの状態だけでスタイルを定義することができるようになりました。
そして、さらに`:has()`と組み合わせることでもっと柔軟にスタイルを書くことができるようになります。
```scss
.item:not(:has(img)) {
  color: #f00;
}
```

```html
<div class="item">
  <img src="*****" alt="">
  ここの文字は赤くならない
</div>
<div class="item">
   ここの文字は赤くなる
</div>
```

と、このように`:not(:has(**))`指定することで`has`の条件ではない要素にスタイルを当てることが可能になります。

# 4. :is()
`:is()`は引数に指定された複数の要素を処理する疑似クラスです。
あまり馴染みがないかもしれませんが、一度理解できるとすごく便利だと思います。
例えば今まで見出しにクラスを指定し、共通のスタイルを定義する場合は以下のコードを書く必要がありました。
```scss
h1.normal,
h2.normal,
h3.normal,
h4.normal,
h5.normal,
h6.normal{
  font-weight: normal;
}
```
ですが、`:is()`を使うことにより、このような書き方ができるようになります。
```scss
:is(h1,h2,h3,h4,h5,h6).normal {
  font-weight: normal;
}
```
ちなみに、現在はCSSのネストを使って似たようなコードが書けるようになっています。
```scss
h1,h2,h3,h4,h5,h6 {
  &.normal{
    font-weight: normal;
  }
}
```
Pleasanterではマークダウンのプレビュー表示のカスタムに利用できそうです。

```scss
.md {
  /*
    > h1,
    > h2,
    > h3,
    > h4,
    > h5,
    > h6 {
      padding: 8px 16px;
      background: #eee;
    }

    マークダウン直下の見出しをスタリングしたい場合、
    通常だと上記のように冗長になるが、:is()を使うときれいにまとまる。
  */

  > :is(h1,h2,h3,h4,h5,h6) {
    padding: 8px 16px;
    background: #eee;
  } 
}

```
このように、前にセレクタがある場合に特に恩恵がある疑似クラスだと思います。

# 5. :empty
```css
td:empty {
  background-color: #ccc;
}
```
こちらも少し前に実装された機能なのでご存じの方も多いかと思います。
指定セレクタの要素が空のとき、つまり子要素を持たない場合に適用される疑似クラスです。
使用例としては上のように一覧画面で空のセルにスタイルを定義したい時に使えそうです。

しかし、先ほど説明した`:has()` `:not()` `:is()`と組み合わせることで、カスタマイズ性が大きく変わります。
上で紹介した疑似クラスをすべて使ってエラー表示をカスタムしてみましょう。
Pleasanterのエラー表示ですが、以下のようなHTML構成になっています。
（見やすいように一部属性は削除）

```html
<div class="field-wide">
  <p class="field-label"><label class="required">タイトル</label></p>
  <div class="field-control">
    <div class="container-normal">
      <input name="Title" class="error" type="text">
      <label class="error">
        入力必須項目です。
      </label>
    </div>
  </div>
</div>
```
エラー文言を表示する`<label class="error"> `はエラーがある場合に出力され、エラーが解消されると文言だけ消えて、`<label class="error">`は空の状態でHTML上に残り続けます。
この条件をセレクタで指定したのがこちらです。

```scss
:is(.field-normal, .field-wide, .field-markdown):has(label.error:not(:empty)) {
  background: #c00;
  .error {
    color: #fff;
  }
}
```
Pleasanterの編集画面ではコントローラを含むクラスは3つあり、`.field-normal, .field-wide, .field-markdown`を`:is()`で指定します。
指定された要素に対し、`:has()`で`label.error`の存在を確認、かつ【中身が空ではない】という処理になる`:not(:empty)`を付与することで、`:is()`で指定したすべての要素にエラー時のスタイルを定義することができます。

ポイントは今までのCSSでは`.error`要素の兄弟/子の要素にしかスタイルを当てることができなかったのですが、`.error`を含む`.field-wide`からでもスタイルを書けるという点ですね。
このように最近実装された疑似クラスを組み合わせることで「空ではない子要素を含む場合のスタイルを定義する」ことが可能になりました。

# 6. コンテナクエリ
少し前まで表示サイズによってスタイルを定義するにはメディアクエリが一般的でしたが、コンテナクエリが全ブラウザに対応したことにより選択肢の一つになりました。
メディアクリアが画面幅を基準にスタイルを定義できるのに対し、コンテナクエリは指定要素の幅を基準にスタイルを定義できます。

使用例として、一覧画面で幅に応じてテーブルのセルを非表示にするように定義してみます。
```scss
.grid-wrap {
  container-type: inline-size;
  :is(th,td) {
    @container (inline-size <= 1200px) {
      &:nth-of-type(4),
      &:nth-of-type(6){
        display: none;      
      }
    }
    @container (inline-size < 1460px) {
      &:nth-of-type(16),
      &:nth-of-type(17){
        display: none;      
      }
    }
  }
}
```
こちらのコードでは`table要素`をwrapしている`.grid-wrap`をコンテナとして宣言します。
```scss
.grid-wrap {
  container-type: inline-size;
}
```
その下層で `:is()`を使い`th`と`td`に対してコンテナクエリの設定を記述します。
コンテナの横幅が1200px以下の場合は4列目と6列目を非表示に、1460px未満の場合は16行目と17行目を非表示にしています。
注意点としては、Pleasanterでは[テーブルの管理：ビュー](https://pleasanter.org/ja/manual/table-management-view)の設定で表示項目をカスタムでき、コンテナクエリで非表示したい列もそれぞれのビューの設定によって変わると思うので、上記のコードを使う際はご注意ください。


 # 7. カスタムプロパティ & var()
 ```scss
 root {
  --pageBg: #eeeeee;
 }
body {
  background-color: var(--pageBg);
}
 ```

次は値を変数のように記述できるカスタムプロパティと、指定した値を挿入できる参照関数`var()`を紹介したいと思います。
Pleasanterの新UIのテーマではテーマごとにカラースキームを定義し、プロパティとして利用しています。

```scss
:root {
  /* ColorScheme ---------------------------------------------------------- */
  --primaryColor: #106ebe;
  --primaryDark: #005a9e;
  --primarySub01: #90caff;
  --primarySub02: #c1e1ff;
  --primarySub03: #deecf9;
  --primarySub04: #eff6fc;

  /* 以下所略 */
}

.ui-widget-header {
  background: var(--primaryColor);
}
```

こちらのカスタムプロパティは上書きすることが可能で、テーブル管理のCSSで指定することによりサイト内での色味を変更することが可能です。
```css
:root {
  --primaryColor: #9a0000;
  --primaryDark: #541d1d;
  --primarySub01: #9a0000;
  --primarySub02: #ba5f5f;
  --primarySub03: #ffe4e4;
  --primarySub04: #d8d8d
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760693/63e1098e-078a-7d34-190e-36e6dd1edcfa.png)

今年、主要ブラウザ全てでサポートされた`@property`を使えばより便利に使うことができるので、興味のある方を是非ご確認ください。
https://developer.mozilla.org/ja/docs/Web/CSS/@property


# 8. attr();
`attr()`は選択された要素に設定してある属性の値を受け取るCSS関数です。
よくCSSだけで作るツールチップとして使われますね。
Pleasanterでもパンくずリストの階層が深い場合に省略時のUIとして使用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760693/d19c19fe-4d52-f2cc-c116-665edb5f4155.png)

では、入力項目の「説明」に設定した値をツールチップで表示してみましょう。


```scss
:is(.field-normal, .field-wide, .field-markdown):has(.field-control[title]) {
  position: relative;
  .field-label:hover {
    + .field-control {
      &::after{
        /* ここでtitle属性のデータを取得 */
        content: attr(title);
        position: absolute;
        bottom: calc(100% + 4px);
        left: 0;
        z-index: 10;
        color: #fff;
        padding: 4px 8px;
        background: rgba(0,0,0,.6);
        border-radius: 4px;
      }
    }
  }
}
```
入力項目に`title`属性が指定されていた場合、疑似要素`::after`の設定で`attr(title)`を取得し、ツールチップの文言として表示させています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760693/d7785d96-0597-904b-9678-d737169fa70d.png)

ただし、`title`属性はブラウザ標準のポップアップとしても出てしまうので、気になる方はJSで`title`属性を`data-title`属性にするなど調整してみてください。


# 9. field-sizing: content;
テキストエリアで文字を入力しているとき、フォームの高さが足りないため前に書いてある文章が確認できず、右下のリサイズハンドルでフォームの高さを調整し、また入力作業に戻る・・・。
そんな経験をした方も多いのではないでしょうか。
今までは入力に応じて高さが自動的に変わるようなJSを自作したり、ライブラリを当てたりしていたかと思いますが、そんな自動可変textareaがたった1行のCSSで簡単に実装できちゃいます。

設定はたったこれだけ。
```scss
textarea {
  field-sizing: content;
}
```
これで自動可変textareaが実装できちゃうなんて、本当にCSSの進化はすごいです。

もうちょっと使いやすくするため、もう少しパラメータを増やしてみます。

```css
textarea {
  field-sizing: content;
  min-height: 4lh;
  max-height: 20lh;
}
```
`lh`という単位は比較的最近追加された単位で、1行の高さを相対的に得ることができます。
`4lh`というのは4行が収まる高さということですね。
上のコードは【最小4行、最大20行で高さが可変】ということになります。

Pleasanterをカスタムし実装する場合は、以下のコードを[テーブルの管理のスタイル](https://pleasanter.org/ja/manual/table-management-style)に登録し、該当するコントローラの詳細をエディタ設定から開き、「コントロールCSS」の項目に `textarea-sizing` と入力するだけです。
```css
.textarea-sizing {
  field-sizing: content;
  min-height: 4lh;
  max-height: 20lh;

  /* リセット用 */
  height: auto!important;
}
```

`field-sizing: content;`は一部のブラウザでまだサポートされていないのですが、ほとんどのブラウザが対応しているので是非使ってみてください！


# 10. position: sticky;
最後になりましたが、こちらもWEBデザインの表現でデザイナーに大きなヒントを与えたパラメータです。
画面をスクロールしたときに要素を上に張り付かせたりする機能になります。
Pleasanterでは一覧テーブルの見出しの部分などに使っていますね。
今まで画面に追従するものは`position: fixed;`を使うのが一般的でしたが、`position: sticky;`でしかできない動きもあるので一度試してみることをお勧めします。

Pleasanterで使う場合はマークダウンプレビュー、特にwikiのカスタムにおすすめです。

```scss
.md {
  position: rlative;
  > :is(h1, h2, h3, h4, h5, h6) {
    position: sticky;
    top: 0;
    width: 100%;
    padding: 8px 16px;
    color: #fff;
    background: #555;
    border-radius: 4px;
    &:is(h1, h2) {
      font-size: 14px;
    }
  }
}

```
こちらの設定を適応した画面がこちらです。
静止画だと少しわかりにくいかもですが、見出しがページの上に張り付き、何の文章を見ているのか分かりやすくなっているかと思います。
このままスクロールしていくと、次は【2. :has()】が上へ張り付くようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760693/5f9c8b18-030d-138a-99b8-a75096269afc.png)


## さいごに
いかがでしたでしょうか。2024年のPleasanterは新UIテーマをリリースし、CSSネスティングをはじめここで紹介したスタイルの多くを取り入れております。

自由すぎるカスタマイズ性もPleasanterの魅力のひとつ。どんどんカスタマイズして自分だけの自慢のPleasanterに育ててみてください！

まだまだ紹介したかったパラメータや機能は沢山ありますが、それはまた次の機会に！
2025年もより便利に、より使いやすいUIを目指して邁進してまいりますので、これからもPleasanterをよろしくお願いします！