かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

CSS 日本語をいい感じにできる line-break プロパティ

WEB サイトのコンテンツの主体は文章です。
その文章を日本語でもエディトリアルの禁則のようにいい感じにしてくれるプロパティを知ったのでメモ

line-break プロパティ

The line-break CSS property sets how to break lines of Chinese, Japanese, or Korean (CJK) text when working with punctuation and symbols.
cf. line-break - CSS: Cascading Style Sheets | MDN

line-break プロパティの値

  • auto (default)
    • Break text using the default line break rule.
  • loose
    • Break text using the least restrictive line break rule. Typically used for short lines, such as in newspapers.
    • 弱い禁則 (一般的に新聞などの短い行に使われる)
  • normal
    • Break text using the most common line break rule.
    • 一般的な改行規則
  • strict
    • Break text using the most stringent line break rule.
    • 強い禁則
  • anywhere
    • There is a soft wrap opportunity around every typographic character unit, including around any punctuation character or preserved white spaces, or in the middle of words, disregarding any prohibition against line breaks, even those introduced by characters with the GL, WJ, or ZWJ character class or mandated by the word-break property. The different wrapping opportunities must not be prioritized. Hyphenation is not applied.
    • どこでも折り返す・単語間で折り返す場合のハイフネーション無し (句読点などの役物が先頭に来ることもある)

書いてても分かりづらいのでサンプルを作った

See the Pen line-break with Japanese text by KIKIKI (@kikiki_kiki) on CodePen.

カギ括弧や句読点、ぁ のような子音の扱いが微妙に変わる。
デフォルトの autoloose でもそんなに悪くないように感じる。strict が紙面のような最も強い禁則ではあるが、テキストの入るボックスサイズによっては改行が多くなってしまう可能性もあるので、コンテンツの入るエリアのサイズを考慮して決めるのが良さそう

おわり ₍ᐢ•ᴗ•ᐢ₎


[参考]

React TypeScript 動的にタグを変えたい

Chakra-UI にあるような as props で動的にタグを変更できるコンポーネントを作りたかった

React.createElement を使う方法

import { createElement, FC, ReactNode } from 'react';

type HeadingProps = {
  as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  children: ReactNode;
}

export const Heading<HeadingProps> = ({ as: Heading = 'h1', children }) => {
  return createElement(
    Heading,
    { className: 'heading'},
    children
  );
}

createElement を使う場合、attribute は第二引数にオブジェクト渡せば良い。ただし拡張した特殊な attribute を使おうとすると上手く設定できない場合があるので注意が必要

ElementType を使う方法

as で渡された文字列を ElementType としてしまえば直接 JSX のタグとして扱える

import { ElementType, FC, ReactNode } from 'react';

type HeadingProps = {
  as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
  children: ReactNode;
}

export const Heading<HeadingProps> = ({ as: tagStr = 'h1', children }) => {
  const Heading = tagStr as ElementType;

  return <Heading className="heading">{children}</Heading>
}

props を ElementType 形にすればどんなタグでも可能にできる

import { ElementType, FC, ReactNode } from 'react';

type HeadingProps = {
  as?: ElementType;
  children: ReactNode;
}

export const Heading<HeadingProps> = ({ as: Heading = 'h1', children }) => {
  return <Heading className="heading">{children}</Heading>
}

HTML タグとデザインを分離したいときはこの方法が良さそう。
as ElementType (Type Assertion) しなくても済むし。  
 
おわり ₍ ᐢ•ᴗ•ᐢ ₎


[参考]

さよならポニーテール ホント好き

TypeScript レスポンシブ時のイベントには MediaQueryListAPI matchMedia が便利だった!

レスポンシブ時の View の変化は基本的に CSS のメディアクエリで行いたいが、 window サイズが大きくなったときにモバイル用のモーダルメニューを閉じるとか、意外とレスポンシブ時に JS でイベントを実行したい時がある

window.resize を使った今までやりかた

const breakpoint = 768 as const;

export const App = () => {
  const timerRef = useRef<number>(0);
  const [isSp, setIsSp] = useState<boolean>(true);

  useEffect(() => {
    const timer = timerRef.current;
    const resizeEvent = () => {
      window.clearTimeout(timer);
      window.setTimeout(() => {
        if (window.innerWidth >= breakpoint) {
          setIsSp(false);
        } else {
          setIsSp(true);
        }
      }, 100);
    };

    // レンダリング時に window サイズをチェックするために呼び出す
    resizeEvent();
    // add addEventListener
    window.addEventListener("resize", resizeEvent);
  
    return () => {
      window.clearTimeout(timer);
      window.removeEventListener("resize", resizeEvent);
    };
  }, []);
  
  return (<div>{isSp? "true" : "false"}</div>)
}

今までは window.resize イベントを監視して特定の window サイズになった際にイベントを発火させていたがイベントを間引く debounde の処理などいろいろと面倒な部分があった
また、debounce させていても window がリサイズされる事にイベントが呼び出されるので処理が多くなってしまう

MediaQueryListAPI matchMedia を使った方法

const breakpoint = 768 as const;

export const App = () => {
  const [isSp, setIsSp] = useState<boolean>(true);

  useEffect(() => {
    const mediaQueryList = window.matchMedia(`(min-width: ${breakpoint}px)`);
    const handleOrientationChange = (
      evt: MediaQueryList | MediaQueryListEvent
    ) => {
      if (evt.matches) {
        setIsSp(false);
      } else {
        setIsSp(true);
      }
    };

    // レンダリング時に window サイズをチェックするために呼び出す
    handleOrientationChange(mediaQueryList);
    // add addEventListener
    mediaQueryList.addEventListener("change", handleOrientationChange);

    return () => {
      mediaQueryList.removeEventListener("change", handleOrientationChange);
    };
  }, []);
  
  return (<div>{isSp? "true" : "false"}</div>)
}

MediaQueryListAPI を使った方法だと設定したブレイクポイントを跨いだタイミングでだけ change イベントが発火される
リスナー関数の引数は初回呼び出し時は MediaQueryList で change イベントから受け取る場合は MediaQueryListEvent になるようなので evt: MediaQueryList | MediaQueryListEvent とした。もっと良い指定方法があるかもしれない。

Sample

次のサンプルでは breakpoint を跨ぐと各エリアのデザインが変更され、発火されたイベントの回数を表示している。
matchMedia を使った方は必要なときだけイベントが発火しているのが分かると思う


[参考]

ウェス・アンダーソンもともと好きだったけど、グランド・ブダペスト・ホテル は本当に良かった!