20201204のCSSに関する記事は10件です。

htmlどれがタグでどれが要素か、各名称

htmlのタグとか要素、cssのプロパティー、プロパティーの値とか何が何かよくわからなくごちゃごちゃになると思うのでまとめました。

要素

//要素
<head>
 <meta>
 <title>
 <link>
 <style>
</head>

<body>
 <footer>
 <header>
 <main> 
 <nav>
 <section>

タグ

href
class

など要素の次に書くのをタグという

<a href="`"></a>
<div class="#"></div>

フッタータグは

<footer>
 <small>
 @ 2020 example.com
</small>

セレクター、プロパティ、規則セット

セレクター
プロパティー: プロパティー値;

div {
coloer: red;
font-size: 80px;
右左2つセットで宣言
3つセットで規則セット

div {
 color: red;
 font-size: 80px;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsで画像をファイルに配置して、表示させ、装飾させる方法

はじめに

 アプリケーションのUIを整えるために画像を使った。配置する場所や装飾の方法を記録しておく。

画像の表示

配置

app/assets/imagesに配置した。

表示させる

 表示は、HTMLに

html.erb
<%= image_tag "ファイル名.png", class:"chat-image" %>

サイズの変更

 サイズの変更はCSSにwidthheightで装飾する。

.chat-image {
  width: 50px;
  opacity: 0.5;
}

opacityによって透明度を変えることもできる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

flexboxで横並びにしたコンテンツの中の最後の要素(ボタン等)は下揃えにする方法

どうも7noteです。横並びコンテンツの各要素の最後だけ下揃えにする方法について

先日ツイッターで見つけた画期的なアイデア・スタイルです。正直ちょっと感動しました。

Before
before.png

After
after.png

ガタガタになってしまうところをきれいに並べることができます!

ソース

index.html
<ul>
  <li>
    <figure class="thumb">
      <img src="sample.png" alt="画像">
    </figure>
    <p class="text">テキストが入ります。テキストが入ります。テキストが入ります。</p>
    <div class="btn">ボタン</div>
  </li>
  <li>
    <figure class="thumb">
      <img src="sample.png" alt="画像">
    </figure>
    <p class="text">テキストが入ります。</p>
    <div class="btn">ボタン</div>
  </li>
  <li>
    <figure class="thumb">
      <img src="sample.png" alt="画像">
    </figure>
    <p class="text">テキストが入ります。</p>
    <div class="btn">ボタン</div>
  </li>
</ul>
style.css
ul {
  margin: 0 0 30px;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

ul li {
  width: 200px;           /* 各コンテンツの横幅 */
  border: 1px solid #000; /* 各コンテンツのborder */
  padding: 10px;          /* 余白の指定 */
  display: flex;          /* 【重要】フレックスボックスにする */
  flex-direction: column; /* 【重要】縦並びにする */
}

ul li .text {
  background: #ccc;       /* 背景色にグレーを指定 */
  margin: 0 0 10px;       /* 下に10pxの余白 */
  padding: 5px;           /* 余白5pxを指定 */
}

ul li .btn {
  border: 1px solid #000; /* borderを指定 */
  border-radius: 20px;    /* 角丸にする */
  text-align: center;     /* 文字を中央寄せ */
  margin-top: auto;       /* 【重要】上に取れるだけの余白 */
}

解説

まず各要素をフレックスボックスにして、flex-direction: column;を指定し、liの中の要素を縦並びにする。
そして、下揃えにしたい最後の要素に対して、margin-top: auto;を指定することで、最大余白を自動で取得するので、それ以外の要素の高さに依存せずに下揃えにすることが可能になります。

まとめ

CSSの様々な組み合わせで、まだまだ知らない組み方が出るのでコーディングは本当に奥が深いし面白いなと思います。
日々精進あるのみです。

おそまつ!

~ Qiitaで毎日投稿中!! ~
【初心者向け】HTML・CSSのちょいテク詰め合わせ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Shopify】liquidを編集してコンタクトフォームをカスタマイズする方法

概要

Shopifyでは、コンタクトフォームをデフォルト機能で簡単に実装することができます。しかし、デフォルト機能で実装することができるコンタクトフォームは、名前・電話番号・メールアドレス・メッセージ必要最低限の情報を入力する欄しかありません。

そこで、今回はliquidファイルを編集して、コンタクトフォームを編集する方法を解説します。

クイックスタート

今回は、すでに作成済みのデモサイトを用いて解説します。使用テーマは「Debut」です。使用しているテーマによってコードは若干異なります。基本的な構成は同じなので、流用は可能です。

スクリーンショット 2020-12-03 19.00.55.png

なお、コンタクトフォームの実装方法については、省略します。詳しい実装方法が知りたい方は、こちらの記事をご覧ください。

【Shopifyマスターへの道】#11 コンタクトフォームを作成しよう

Shopifyマスターへの道は、Shopifyの構築を網羅的に解説しているシリーズ連載になリます。興味があれば、そちらも覗いてみてください。

編集するliquidファイルは、page.contact.liquidです。

スクリーンショット 2020-12-03 18.54.22.png

ここにコードを追加することで、コンタクトフォームに入力欄を追加していきます。

page.contact.liquidの構造について

まず、page.contact.liquidの構造について理解します。まずコードの全体像をみます。

<div class="page-width">
  <div class="grid">
    <div class="grid__item medium-up--five-sixths medium-up--push-one-twelfth">
      <div class="section-header text-center">
        <h1>{{ page.title }}</h1>
      </div>

      {% if page.content.size > 0 %}
        <div class="rte">
          {{ page.content }}
        </div>
      {% endif %}

      <div class="contact-form form-vertical">
        {%- assign formId = 'ContactForm' -%}
        {% form 'contact', id: formId %}
          {% include 'form-status', form: form, form_id: formId %}

          <div class="grid grid--half-gutters">
            <div class="grid__item medium-up--one-half">
              <label for="{{ formId }}-name">{{ 'contact.form.name' | t }}</label>
              <input type="text" id="{{ formId }}-name" name="contact[{{ 'contact.form.name' | t }}]" value="{% if form[name] %}{{ form[name] }}{% elsif customer %}{{ customer.name }}{% endif %}">
            </div>
            <div class="grid__item medium-up--one-half">
              <label for="{{ formId }}-email">{{ 'contact.form.email' | t }} <span aria-hidden="true">*</span></label>
              <input
                type="email"
                id="{{ formId }}-email"
                name="contact[email]"
                autocorrect="off"
                autocapitalize="off"
                value="{% if form.email %}{{ form.email }}{% elsif customer %}{{ customer.email }}{% endif %}"
                aria-required="true"
                {%- if form.errors contains 'email' -%}
                  class="input--error"
                  aria-invalid="true"
                  aria-describedby="{{ formId }}-email-error"
                {%- endif -%}
                >
              {%- if form.errors contains 'email' -%}
                <span id="{{ formId}}-email-error" class="input-error-message">
                  <span class="visually-hidden">{{ 'general.accessibility.error' | t }} </span>
                  {% include 'icon-error' %}
                  <span>{{ form.errors.translated_fields['email'] | capitalize }} {{ form.errors.messages['email'] }}.</span>
                </span>
              {%- endif -%}
            </div>
          </div>

          <label for="{{ formId }}-phone">{{ 'contact.form.phone' | t }}</label>
          <input type="tel" id="{{ formId }}-phone" name="contact[{{ 'contact.form.phone' | t }}]" pattern="[0-9\-]*" value="{% if form[phone] %}{{ form[phone] }}{% elsif customer %}{{ customer.phone }}{% endif %}">

          <label for="{{ formId }}-message">{{ 'contact.form.message' | t }}</label>
          <textarea rows="10" id="{{ formId }}-message" name="contact[{{ 'contact.form.message' | t }}]">{% if form.body %}{{ form.body }}{% endif %}</textarea>

          <input type="submit" class="btn" value="{{ 'contact.form.submit' | t }}">

        {% endform %}
      </div>
    </div>
  </div>
</div>

コードを読んでみると、page.contact.liquidは主に3つのブロックからなっています。

  • タイトルを表示させるブロック
  • コンテンツの内容を表示させるブロック
  • コンタクトフォームを表示させるブロック

それぞれのコードをみましょう。

タイトルを表示させるブロック

次のコードで、ページを作成する際に入力したタイトルを表示させています。

      <div class="section-header text-center">
        <h1>{{ page.title }}</h1>
      </div>

<h1>タグのなかで{{ page.title }}を呼び出しています。

コンテンツの内容を表示させるブロック

次のコードで、ページを作成した際に入力したコンテンツの内容を表示しています。

      {% if page.content.size > 0 %}
        <div class="rte">
          {{ page.content }}
        </div>
      {% endif %}

{% if page.content.size > 0 %}でコンテンツの内容が入力されているか判定しています。trueの場合、{{ page.content }}コンテンツの内容を呼び出して表示しています。

コンタクトフォームを表示させるブロック

次のコードで、コンタクトフォームの表示を実装しています。

      <div class="contact-form form-vertical">
        {%- assign formId = 'ContactForm' -%}
        {% form 'contact', id: formId %}
          {% include 'form-status', form: form, form_id: formId %}

          <div class="grid grid--half-gutters">
            <div class="grid__item medium-up--one-half">
              <label for="{{ formId }}-name">{{ 'contact.form.name' | t }}</label>
              <input type="text" id="{{ formId }}-name" name="contact[{{ 'contact.form.name' | t }}]" value="{% if form[name] %}{{ form[name] }}{% elsif customer %}{{ customer.name }}{% endif %}">
            </div>
            <div class="grid__item medium-up--one-half">
              <label for="{{ formId }}-email">{{ 'contact.form.email' | t }} <span aria-hidden="true">*</span></label>
              <input
                type="email"
                id="{{ formId }}-email"
                name="contact[email]"
                autocorrect="off"
                autocapitalize="off"
                value="{% if form.email %}{{ form.email }}{% elsif customer %}{{ customer.email }}{% endif %}"
                aria-required="true"
                {%- if form.errors contains 'email' -%}
                  class="input--error"
                  aria-invalid="true"
                  aria-describedby="{{ formId }}-email-error"
                {%- endif -%}
                >
              {%- if form.errors contains 'email' -%}
                <span id="{{ formId}}-email-error" class="input-error-message">
                  <span class="visually-hidden">{{ 'general.accessibility.error' | t }} </span>
                  {% include 'icon-error' %}
                  <span>{{ form.errors.translated_fields['email'] | capitalize }} {{ form.errors.messages['email'] }}.</span>
                </span>
              {%- endif -%}
            </div>
          </div>

          <label for="{{ formId }}-phone">{{ 'contact.form.phone' | t }}</label>
          <input type="tel" id="{{ formId }}-phone" name="contact[{{ 'contact.form.phone' | t }}]" pattern="[0-9\-]*" value="{% if form[phone] %}{{ form[phone] }}{% elsif customer %}{{ customer.phone }}{% endif %}">

          <label for="{{ formId }}-message">{{ 'contact.form.message' | t }}</label>
          <textarea rows="10" id="{{ formId }}-message" name="contact[{{ 'contact.form.message' | t }}]">{% if form.body %}{{ form.body }}{% endif %}</textarea>

          <input type="submit" class="btn" value="{{ 'contact.form.submit' | t }}">

        {% endform %}
      </div>

{% form 'contact', id: formId %}から{% endform %}までが。ストアに送信される部分になります。
この部分には、4つの<input>タグがあり、それぞれ名前・Eメール・電話番号・メッセージを受け取ることができます。

つまり、この{% form 'contact', id: formId %}から{% endform %}の間に、<input>タグなどを増やすことで、コンタクトフォームをカスタマイズすることができます。

名前のフリガナの入力欄を作成する

シンプルな入力欄を増やします。フリガナを入力するための<input>タグを書きます。<input>タグの書き方は、HTMLの知識になってくるので、ここでの詳しい説明は割愛します。

名前の横に配置したいので、名前の<div>タグとEメールの<div>タグの間に記述します。<div class="grid__item medium-up--one-half">という<div>タグで囲うことで、半分のカラム幅の入力欄を実装できます。

以下のコードを追記しましょう。

<div class="grid__item medium-up--one-half">
  <label for="{{ formId}}-furigana">フリガナ</label>
  <input type="text" id="{{ formId}}-furigana" name="contact[フリガナ]" >
</div>

スクリーンショット 2020-12-04 10.18.37.png

このように追記できたら、一旦コードを保存しプレビュー画面で確認します。

スクリーンショット 2020-12-04 10.19.25.png

このように実装できました。Eメールの隣が空いてしまっているので、そこに新たな入力欄を追加します。

性別の選択欄を作成する

<select>タグを用いて、性別の選択欄を作成します。<select>タグを用いると、ドロップダウン式の選択欄を作成することができます。<select>タグの中に<option>タグで選択肢を指定していきます。

以下のコードをEメールの入力欄の後ろに記述してください。

<div class="grid__item medium-up--one-half">
  <label for="{{ formId}}-sex" >性別</label>
  <select id="{{ formId}}-sex" name="contact[性別]">
    <option>--選択してください--</option>
    <option>男性</option>
    <option>女性</option>
    <option>その他</option>
  </select>
</div>

スクリーンショット 2020-12-04 14.21.31.png

追記したらコードを保存し、プレビュー画面で確認しましょう。

スクリーンショット 2020-12-04 14.22.30.png

電話番号の入力欄を削除する

元からある入力欄を削除してみましょう。例として電話番号の入力欄を削除してみます。

以下のコードを削除してください。

<label for="{{ formId }}-phone">{{ 'contact.form.phone' | t }}</label>
<input type="tel" id="{{ formId }}-phone" name="contact[{{ 'contact.form.phone' | t }}]" pattern="[0-9\-]*" value="{% if form[phone] %}{{ form[phone] }}{% elsif customer %}{{ customer.phone }}{% endif %}">         

削除したら保存し、プレビュー画面で確認します。

スクリーンショット 2020-12-04 14.00.17.png

問題なく削除することができました。

チェックボックスを作成する

次に、<input>タグのtype="checbox"属性を使って、チェックボックスを実装します。

以下のコードを追記します。

<p>お問い合わせ内容を選択してください(複数選択可)</p>
<label for="{{ formId}}-inquirydetails"><input type="checkbox" id="{{ formId }}-inquirydetails" name="contact[お問い合わせ内容]" value="お仕事のご依頼">お仕事のご依頼</label>
label for="{{ formId}}-inquirydetails"><input type="checkbox" id="{{ formId }}-inquirydetails" name="contact[お問い合わせ内容]" value="無料相談">無料相談</label>
<label for="{{ formId}}-inquirydetails"><input type="checkbox" id="{{ formId }}-inquirydetails" name="contact[お問い合わせ内容]" value="その他">その他</label><br>

スクリーンショット 2020-12-04 14.50.45.png

コードを保存して、確認しましょう。
スクリーンショット 2020-12-04 14.53.32.png

このようにチェックボックスを実装することができました。

以上で、フォームの編集は終了です。

送信できることを確認する

送信フォームが正常に送信することができるか、確認します。作成したコンタクトフォームで、必要事項を入力し送信します。
スクリーンショット 2020-12-04 14.59.13.png

以下のようなメッセージが表示されれば送信は、正常に完了しています。
スクリーンショット 2020-12-04 14.55.21.png

実際に届いたメールはこのようになっています。
ScreenClip.png

最後まで読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Less CSSで@importを展開せずにそのままCSSにする方法

Less CSSで@importを展開せずにそのままCSSにする方法

ちょっと詰まったので備忘録として。

Less CSSで@importをそのまま使うと、外部のサイトでもインポートして展開されてしまう。lesscで変換しようにも社内のプロキシ環境で使うとコネクションエラーが発生するし、Google Fontsを使うときも展開しては欲しくない。less.jsに動的に変換させればよいのだが、Internet Explorer 11では動かない。これは困った。

探していたら、ちゃんとマニュアル (英語) に書いてあった。このcssオプションを使えば展開されずにそのままCSSに出力される。

sample.less
/* フォント設定 */
@import (css) url(https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Serif+JP|Roboto+Mono&display=swap);
sample.css
/* フォント設定 */
@import url(https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Serif+JP|Roboto+Mono&display=swap);

Microsoftが自社サービスのInternet Explorerサポート終了を打ち出したので、Internet Explorerはもうそろそろ考えなくても良いかな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lessで@importを展開せずにそのままCSSにする方法

Lessで@importを展開せずにそのままCSSにする方法

ちょっと詰まったので備忘録として。

Lessで@importをそのまま使うと、外部のサイトでもインポートして展開されてしまう。lesscで変換しようにも社内のプロキシ環境で使うとコネクションエラーが発生するし、Google Fontsを使うときも展開しては欲しくない。less.jsに動的に変換させればよいのだが、Internet Explorer 11では動かない。これは困った。

探していたら、ちゃんとマニュアル (英語) に書いてあった。このcssオプションを使えば展開されずにそのままCSSに出力される。

sample.less
/* フォント設定 */
@import (css) url(https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Serif+JP|Roboto+Mono&display=swap);
sample.css
/* フォント設定 */
@import url(https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Serif+JP|Roboto+Mono&display=swap);

Microsoftが自社サービスのInternet Explorerサポート終了を打ち出したので、Internet Explorerはもうそろそろ考えなくても良いかな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lessで@importを展開しないでそのままCSSにする方法

ちょっと詰まったので備忘録として。

Lessで@importをそのまま使うと、外部のサイトでもインポートして展開されてしまう。lesscで変換しようにも社内のプロキシ環境で使うとコネクションエラーが発生するし、Google Fontsを使うときも展開しては欲しくない。less.jsに動的に変換させればよいのだが、Internet Explorer 11では動かない。これは困った。

探していたら、ちゃんとマニュアル (英語) に書いてあった。このcssオプションを使えば展開されずにそのままCSSに出力される。

sample.less
/* フォント設定 */
@import (css) url(https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Serif+JP|Roboto+Mono&display=swap);
sample.css
/* フォント設定 */
@import url(https://fonts.googleapis.com/css?family=Noto+Sans+JP|Noto+Serif+JP|Roboto+Mono&display=swap);

Microsoftが自社サービスのInternet Explorerサポート終了を打ち出したので、Internet Explorerはもうそろそろ考えなくても良いかな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

odoo - Fontの変更

Custom font という仕組み。

自動生成される web.assets_backend.css の body { Font-Family } に IPAmjMincho を追加。
font-family: "Odoo Unicode Support Noto", "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;

生成されたcss
http://localhost:8069/web/content/235-a50e7a9/web.assets_backend.css

"Odoo Unicode Support Noto"が出現する箇所。
/* /web/static/src/scss/import_bootstrap.scss defined in bundle 'web.assets_backend' /
/
/web/static/src/scss/webclient.scss defined in bundle 'web.assets_backend' */
.o_web_accesskey_overlay{font-family: "Odoo Unicode Support Noto", "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;}

addons\web\static\src\scss\import_bootstrap.scss
???
addons\web\static\src\scss\webclient.scss
.o_web_accesskey_overlay {
    font-family: $font-family-sans-serif;
}
addons\web\static\src\scss\bootstrap_overridden.scss
// Fonts
//
// Font, line-height, and color for body text, headings, and more.

$font-family-sans-serif: o-add-unicode-support-font(("Lucida Grande", Helvetica, Verdana, Arial, sans-serif), 1) !default;

addons\web\static\src\scss\utils.scss
// Odoo defines a limited Noto font-family for a small variety of unicode
// characters that are not necessary defined in the user system or even defined
// but not properly readable. This function allows to add this font family in a
// given font list.
//
// @param {list} $font - a list of font names ending with the generic one.
// @param {integer} [$index] - the position where to add the support font, if
//        not given, it will be placed before the generic one.
@function o-add-unicode-support-font($font, $index: false) {
    @if $index == false {
        $index: length($font);
    }

    $-with-support-font: ();
    @for $i from 1 through length($font) {
        @if $i == $index {
            $-with-support-font: append($-with-support-font, 'Odoo Unicode Support Noto', $separator: comma);
        }
        $-with-support-font: append($-with-support-font, nth($font, $i), $separator: comma);
    }

    @return $-with-support-font;
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Tailwind CSS 2.0で追加された機能全部使ってデモページを作る

この記事は弥生 Advent Calendar 2020の4日目の記事です。

さて、2020年11月19日にTailwind CSSのver2.0がリリースされました???
https://blog.tailwindcss.com/tailwindcss-v2

それを記念して? Tailwind CSS 2.0で追加された機能全部入れてデモページを作ってみました。アドベントカレンダー記事というのもあり、Qiitaアドベントカレンダーをインスパイアした形で作っています。

TailwindCSSとは何ぞやという話は、いろんな方がすでにたくさん記事を書いていますので割愛します。

(ご参考)

作ったもの

アドベントカレンダーのページっぽく、ヘッダー、タイトル、カレンダー、フッターを含んだ1ページです。レスポンシブ、ダークモード対応しています。生のCSSは1行も書いていません。全てTailwindCSSだけで作っています。

なお、Tailwind Playというサンドボックス環境に公開していますので、こちらから作ったものとソースコードがみれます。(開いたらビルドが走るみたいですので、初回表示は遅いです。)
https://play.tailwindcss.com/euCH5tu0IA?layout=horizontal

demo.gif

chromeの開発ツールでブラウザのダークモード切り替えが出来ますので、遊んでいただければと思います。(Tailwind Playヘッダーの月と太陽マーク押してもスイッチしないので注意)

Tailwind CSS 2.0で追加された機能

ニュースリリースからの引用です。

  • All-new color palette
  • Dark mode support
  • Extra wide 2XL breakpoint
  • New outline ring utilities
  • Utility-friendly form styles
  • Default line-heights per font-size
  • Extended spacing, typography, and opacity scales
  • Use @apply with anything
  • New text overflow utilities
  • Extend variants
  • Group-hover and focus-within by default
  • Default transition duration and easing curve
  • Incompatibility with IE11

これらを全部デモページに埋め込みましたので、一つ一つ実装したものを見ていきたいと思います。

All-new color palette

デフォルトで用意されているカラーパレットが全面的に刷新され、今までの10色、9段階の明暗のカラーパレットから、22色、10段階の明暗が用意されました。
image.png
デモページではcyanを使ってみました。色の追加はtailwind.config.jsという設定ファイルをいじります。

tailwind.config.js
const colors = require('tailwindcss/colors')

module.exports = {
  theme: {
    colors: {
      cyan: colors.cyan,
    },
  },
}

image.png

<span class="block text-cyan-600 dark:text-cyan-500 xl:inline text-6xl"> 2020 </span>

色はこんな感じです。いい色ですね。

Dark mode support

簡単にダークモード対応出来るようになりました。設定ファイルに1行追加して、

tailwind.config.js
module.exports = {
  darkMode: 'media',
  // ...
}
<header class="h-16 bg-white dark:bg-gray-800 px-4">
</header>

こんな感じでdark:ディレクティブを使って記述すれば、ブラウザのダークモード設定時にはdark:のスタイルが使用されます。簡単!すごい!

Extra wide 2XL breakpoint

1536px以上の画面サイズに対する新しいブレイクポイント2xl:が追加されました。デモページでは画面サイズを1536px以上にすると文字が出てくるようにしてみました。

<p class="hidden 2xl:block text-9xl text-center font-bold my-20">Too wide and Too Big!!</p>

toowide.gif

New outline ring utilities

入力フォームなどでフォーカスされた時に出てくる黒い枠のところをoutline ringと呼んでいます。このスタイルが増えました。

<input type="text" placeholder="ユーザーID" class="rounded border mr-2 px-2 py-1 focus:outline-none focus:ring-4 focus:ring-cyan-300 focus:ring-opacity-75" />

これをフォーカスすると、、
スクリーンショット 2020-12-01 20.43.09.png
こんな感じにできます。
スクリーンショット 2020-12-01 20.43.17.png

Utility-friendly form styles

ブラウザごとに異なるフォームデザインをノーマライズ・リセットしてTailwindのユーティリティクラスをシンプルに使用できるようになりました。
@tailwindcss/formsプラグインを使用するようにconfigファイルに記述します。

tailwind.config.js
module.exports = {
  // ...
  plugins: [require('@tailwindcss/forms')],
}
<input type="checkbox" class="rounded-full mx-1 text-cyan-600 focus:outline-none" />
<input type="checkbox" class="rounded-full mx-1 text-cyan-600 focus:outline-none" />
<input type="checkbox" class="rounded-full mx-1 text-cyan-600 focus:outline-none" />
<input type="checkbox" class="rounded-full mx-1 text-cyan-600 focus:outline-none" />
<input type="checkbox" class="rounded-full mx-1 text-cyan-600 focus:outline-none" />

スクリーンショット 2020-12-01 21.20.33.png
ユーザビリティは悪いですが、丸いチェックボックスなんかも作れますし、チェックした色も簡単に変えられます。これ、地味に凄くないですか?今まではチェックボックスの色変えようと思ったらCSSたくさん書いて自前でチェックの形作ってました。それがtext-cyan-600だけで色変わります。

Default line-heights per font-size

フォントサイズを選んだ時にデフォルトでLine Heightも設定されるようになりました。

<p class="text-xl">This will have a line-height of 1.75rem automatically.</p>

Extended spacing, typography, and opacity scales

デフォルトで選べるサイズが増えました。
96とかはあまり使わないと思いますが、7とかはたまに使いたい場面があったので良かったと思います。

テキストサイズも9xlまで用意されています。ちなみにブレイクポイントのところで出した「Too Big!」の文字はtext-9xlを使っています。
toowide.gif

Use @apply with anything

普通のセマンティックなクラスを作りたい場合、@applyディレクティブを使って共通化できましたが、今まではfocus:hover:などの一部の機能が使えませんでした。今回から全ての機能を@applyに使用できます。もちろんdark:もです。

<button class="btn-cyan">参加登録</button>
.btn-cyan {
  @apply bg-cyan-600 dark:bg-cyan-500 text-white text-sm px-2 py-1 rounded mt-2 hover:bg-cyan-700;
}

New text overflow utilities

今まではテキストが要素からはみ出した時にtruncateしかありませんでしたが、overflow-ellipsisoverflow-clipが使えるようになりました。ただ、これは日本語には使えないかもしれないです。英語はうまく...が入りましたが、日本語はうまくいきませんでした。

<div class="h-20 w-24 overflow-hidden overflow-ellipsis text-xs mt-1">
  <a class="text-cyan-600 dark:text-cyan-500 hover:underline cursor-pointer">This is a long long English Sentences and LongLongLongLongOneWord</a>
</div>

スクリーンショット 2020-12-01 22.16.49.png

Group-hover and focus-within by default

グループを指定して、その要素がホバーされたら子要素にもホバー時の挙動が適用できます。

<td class="group h-40 w-1/7 border-r text-gray-600 dark:text-gray-300 hover:bg-gray-200">
  <div class="flex flex-col h-full p-2">
    <div class="text-center">8</div>
    <div class="flex items-center text-xs mt-2 hover:underline">
      <div class="h-5 w-5 rounded-full bg-gray-500 group-hover:bg-cyan-600 mr-1"></div>
      <a href="#" class="text-cyan-600 group-hover:text-gray-500 dark:text-cyan-500"> User Name </a>
    </div>
    <div class="text-xs text-gray-400 mt-1 group-hover:text-red-400">group hover確認</div>
  </div>
</td>

hover.gif
子要素は子要素でホバーしたときの挙動を変えられます。便利。

Default transition duration and easing curve

transitionとeaseのデフォルト値を定義できるようになりました。tailwind.config.jsにtransitionDuration = 1000msを試しに追加してみます。

tailwind.config.js
module.exports = {
  darkMode: "media",
  theme: {
    //...//
    transitionDuration: {
      DEFAULT: '1000ms',
    },
    transitionTimingFunction: {
      DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',
    },
  },
  variants: {},
  plugins: [require('@tailwindcss/forms')],
}
<button class="bg-cyan-600 rounded text-white px-3 py-2 transition  hover:bg-white hover:text-cyan-600 shadow-lg focus:outline-none">招待する</button>

transitionクラスを追加するだけでいい感じになりました。アニメーションの一貫性を維持しやすくなりますね。
transition.gif

以上!

2.0で追加された機能を全部試してみました。

個人的にはダークモードがやっぱり楽すぎるのと、@apply全機能対応が嬉しかったです。

最後までお付き合いいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FNS歌謡祭のHPに実装されているアニメーションを自分も作成したい!!! ハンズオン!!

はじめに

お疲れ様です。
DMM WEBCAMP Advent Calendar 2020の4日目を担当させていただきます。:fist:
メンターの@koseiinfratopです。:basketball_player_tone5:

 みなさん先日OAされたFNS歌謡祭はみましたか?:dancer_tone4:
バンタンや3代目JSBなど豪華アーティストが出演されていて本当におもしろい番組でしたね。なかでもユーミンと嵐のコラボは感動的でした。:guardsman_tone4:
OA当日僕はふと思いました。嵐はいつごろ出番なのだろうかと。:rolling_eyes:

そこでFNS歌謡祭の公式HPを訪れたことで今回のAdvent Calendarで何を書くがが決定しました。


一度公式HPを訪れていただけるとわかるのですが、アーティストの画像にカーソルを乗せてみてください。カーソルを乗せると薄黒いボックスとアーティスト名が出てきます。僕は疑問に思いました。これはどうやって実装しているのかと。。。
ということで今回は疑問に思ったことを解消するためにFNSのHPのようにカーソルを画像に載せるとアニメーションが発火するような機能を実装しようと思います。

↓今回実装したアニメーション
qiita_2.gif


  • 開発効率を上げたかったため今回はホットリロードが可能なReact.jsでコーディングしました。
  • しれっとiTunesAPIも使用しています。


1.html(js)を記述

music.js
 ...*iTunesAPI関係の処理は割愛

return(


<div className="searchresult">
  {artistData.map(artistdata => (
    <ArtistData 
      key={artistdata.CollectionId.toString()}
      id={artistdata.CollectionId}
      name={artistdata.ArtistName}
      album={artistdata.AlbumName}
      albumUrl={artistdata.AlbumUrl}
      genre={artistdata.AlbumGenre}
      release={artistdata.AlbumRelease}               
    />

    )
   )}
</div>
);
artistdata.js
import React from 'react';

const ArtistData = (props) => {

    return(

        <div className={props.id ? 'album': 'noalbum'}>
            <div className="flex">
                <img src={props.albumUrl} alt={props.album} className="albumImage"/>
                <div>
                    <p>ジャンル: <b>{props.genre}</b></p>
                </div>

            </div>
            <div className="mask">
                <div className="caption">{props.album}</div>
            </div>
            <div>
                <p>アーティスト: <b>{props.name}</b></p>
                <p>アルバム名: <b>{props.album}</b></p>
                <p>リリース日: <b>{props.release}</b></p>
            </div>



        </div>
    );
}

export default ArtistData

2.CSSの記述

*ポイントだけコメントアウトを用いて解説します。

music.css
.album {
    width:          300px;
    height:         230px;
  /* overflow・・・アルバムクラスのdivタグ範囲内に内容が収まらない場合(今回で言うとmaskクラスのdivタグがはみ出る)の処理
    overflow:       hidden; /* 表示させないようにしている */

    margin:         10px 8px 10px 16px;
    position:      relative; 
    border:        ridge 10px #87CEFA;
}
.mask {
    width:          100%;
    height:         100%;
    position:       absolute;
    top:            -100%; /* 枠の上に配置し非表示にする。 */
    opacity:            0;  /* マスクスクラス内を透明化(0)にすることで非表示にする。*/

    background-color:   rgba(0,0,0,0.4);
    transition:         all 0.6s ease; 
}  

.caption {
    font-size:      130%;
    text-align:    center;
    color:          #fff;
}

img.alabumImage {
    width: 80%;
    height: 80%;
    position: absolute;
}

.album:hover .mask {  アルバムクラス内をhover(カーソルを乗せる)時に発火する。
    opacity:        1;  /* マスクを完全に不透明表示にする */
    padding-top:        80px;   /* ホバーで下にずらす */
    top: 0; /* 先ほどのtop: -100% から top: 0;にすることにより下から降りてくるように見せることができる */
}

完成!!!


デモンストレーション


qiita_article.gif

詳細な動きform内にアーティスト(曲名も可)を入力し検索ボタンをクリックすることでitunesapiからアーティストが発売したアルバムを取得し、表示しています。

今回のmusic.js, music.cssの全コード(※artistdata.jsは上記のコードが全コードです。)

music.js
import React, {useState } from 'react';
import axios from 'axios';
import ArtistData from './artistdata.js';
import '../styles/music.css'
const Music = () => {
    const [artist, setArtist] = useState('');
    const [artistData, setArtistData] = useState([{
        CollectionId: '',
        ArtistName: '',
        AlbumName: '',
        AlbumUrl: '',
        AlbumGenre: '',
        AlbumRelease: '',
    }]);

    async function itunesGet(params){
        try{
            const prm = params.trim();
            const response = await axios.get(`https://itunes.apple.com/search?term=${prm}&entity=album`)
            const responsedata = response.data.results
            const responseAPI = responsedata.map(value => {
                return {
                    CollectionId: value.collectionId, 
                    ArtistName: value.artistName,
                    AlbumName: value.collectionName, 
                    AlbumUrl: value.artworkUrl100,
                    AlbumGenre: value.primaryGenreName,
                    AlbumRelease: value.releaseDate,

                };
            }
            );
            setArtistData(responseAPI);
            console.log(artistData);
        }catch(error) {
            const {
                status, statusText
            } = error.response;
            console.log(`Error! HTTP Status: ${status} ${statusText}`)

        }

    };

    return (
        <div>
            <form
                onSubmit = {e => {
                    e.preventDefault();
                    const artistnameElement = e.target.elements["artist"];
                    console.log(artistnameElement.value);

                    itunesGet(artistnameElement.value);

                    setArtist(artistnameElement.value);

                    artistnameElement.value = '';

                }}
            >
                <input type="text" id="artist"
                    placeholder="アーティスト名または曲名を入力してください"
                />
                <button type="submit">検索する</button>


            </form>
            <p className="result">検索結果: <b>{artist}</b></p>
            <div className="searchresult">
            {artistData.map(artistdata => (
                <ArtistData 
                    key={artistdata.CollectionId.toString()}
                    id={artistdata.CollectionId}
                    name={artistdata.ArtistName}
                    album={artistdata.AlbumName}
                    albumUrl={artistdata.AlbumUrl}
                    genre={artistdata.AlbumGenre}
                    release={artistdata.AlbumRelease}

                />

            )
            )}
            </div>

        </div>
    )
}

export default Music
music.css
* {
    margin: 0 auto;
    padding: 0;
    box-sizing: border-box;
}


form > :first-child  {
    outline: none;
    border: 1px solid #aaa;
    transition: all .3s;
    border-radius: 2px;
  }
form > :first-child {
    width: 400px;
    font-size: 18px;
    height: 24px;
    padding: 2px 8px;
  }
form > :nth-child(1):focus {
    box-shadow: 0 0 7px #1abc9c;
    border: 1px solid #1abc9c;
}

form > :last-child {
    margin-top: 4px;
    margin-left: 7px;
    font-size: 16px;
    height: 40px;
    padding: 2px 8px;
}
form button {
    border: 1px solid #ccc;
    background-color: #FFFFFF;

    border-radius: 2px;
    cursor: pointer;
    box-shadow: 0px 2px 2px 0px rgba(0,0,0,.1);
}
form button:hover {
    box-shadow: 0px 2px 2px 2px rgba(0, 0, 0, .1);

}
.result {
    text-align: center;
}
.searchresult {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
}
.noalbum{
    display: none;
}
.flex {
    padding: 3px 1px;
    display: flex;
    flex-direction: row;
}
.flex > :nth-child(2) {
    padding-top: 1.7em;
    padding-bottom: 0.5em;


}
.flex > :nth-child(2):nth-child(2){
    font-family: 'Courier New', Courier, monospace;
}

.album {
    width:          300px;
    height:         230px;
    overflow:       hidden; 
    margin:         10px 8px 10px 16px;
    position: relative;
    border: ridge 10px #87CEFA;
}
.mask {
    width:          100%;
    height:         100%;
    position:       absolute;
    top:            -100%;
    opacity:            0; /* マスクにする */

    background-color:   rgba(0,0,0,0.4);
    transition:     all 0.6s ease;
}  

.caption {
    font-size:      130%;
    text-align:         center;
    color:          #fff;
}

img.alabumImage {
    width: 80%;
    height: 80%;
    position: absolute;
}

.album:hover .mask {
    opacity:        1;  /* マスクを完全に不透明表示する */
    padding-top:        80px;   /* ホバーで下にずらす */
    top: 0;
}

まとめ

- 「このサイト、アプリはどのように実装されているのだろうか?」と疑問を持ち実際に自分で実装することで自分の技術力UPに繋がるということを改めて知ることができたそんな師走上旬でした。。。

裏話FNS公式HPのデベロッパーツールのSourceパネルと睨めっこしながら実装したのは内緒。。。



参考資料

2020 FNS歌謡祭 - フジテレビ
itunes api

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む