GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

ログイン 画面 デザイン9選 — 会員登録からセッション切れまで

「ログイン 画面 デザイン」を探すと出てくる多くの参考画像は、色や余白を真似るだけの静止した見本です。ここで扱う9つの画面はその手前に計算の層を1枚重ねます。入力の完成度で進捗表示が求め直され、残り有効時間が毎秒求め直されてリングの角度になり、スクロール進捗が一定地点を超えて初めて同意チェックが開く、ログイン・会員登録

自動再生 · タイルを押すとその項目へ · すべて1つのzip

デモ

9項目とも「見た目の演出ではなく、入力欄の完成度・ステップ進捗率・プロバイダー数・残り有効時間・桁の充填比率・メールの有効性スコア・経過時間・一致率・スクロール進捗という値を毎フレーム求め直して進捗リング・バーの長さ・列数・ゲージの角度・ボタン状態・バッジの色に反映する」という同じ前提を共有しています。実測値は次のとおりです。

番号 項目 累積面積 強度平均 動いたフレーム
01 ログインカード 0.82% 155.2 19/23
02 会員登録ステップ 2.29% 177.4 23/23
03 ソーシャルログイン 23.17% 161.0 23/23
04 マジックリンク 2.65% 153.0 23/23
05 2段階認証 9.23% 49.4 22/23
06 パスワード再設定リクエスト 7.96% 159.4 20/23
07 セッション期限切れ 4.85% 31.8 23/23
08 キャプチャ枠 4.77% 71.6 7/23
09 同意チェックボックス 15.53% 144.1 23/23

03ソーシャルログインが累積面積23.17%で最も広いのは、プロバイダー数が切り替わるたびに列数を組み替えて予告バーまで一緒に動くためです。09同意チェックボックスの15.53%がそれに続きます。01ログインカード・02会員登録ステップ・04マジックリンクの面積が2%台と狭いのは、入力欄・進捗バー・リングのように構造上わずかな領域だけが変わるのが自然な値で、欠陥ではありません(コンポーネントの精巧さ基準)。08キャプチャ枠が7/23で最も動いたフレームが少ないのも、正解と関係のない自動選択の合間に待機区間が挟まる構造上の性質で、こちらも欠陥ではありません。強度平均は07セッション期限切れが31.8で最も落ち着いており(バッジの色が淡い色調の中だけで濃くなる)、02会員登録ステップが177.4で最も明るくなっています(太い進捗バーが濃い色ですばやく満ちる)。9項目とも下限に対して十分な余裕があります。実際のユーザー操作に反応するのは09同意チェックボックス(規約パネルの本物のscrollイベント)だけで、残り8項目はrequestAnimationFrameの自動再生だけで示されます。

ログイン・会員登録UIの解剖 — 9つの画面が計算するもの

「ログイン 画面 デザイン おしゃれ」で検索して出てくるもののほとんどは、色や角丸の見た目だけを整えた静的なカードです。ここで扱う9つの画面は、その手前に計算の層を1枚重ねます。入力欄の完成度・ステップ進捗率・プロバイダー数・残り有効時間・桁の充填比率・メールの有効性スコア・経過時間・一致率・スクロール進捗というデータを毎回読み直し、進捗リング・バーの長さ・列数・ゲージの角度・ボタン状態・バッジの色・有効化のタイミングに変換し直す設計です。

何が入力で何が出力か

入力は入力欄に埋まった文字数・現在のステップ番号・プロバイダーボタンの個数・送信からの経過ミリ秒・埋まった桁数・メールの文字構成・セッションが切れてからの経過時間・選択された枠の位置・スクロール量の9種類です。出力はCSS変数(--ratioや--colsなど)か、DOMに直接書き込む属性・テキストのどちらかで、レイアウトのキーフレーム自体は変えません。

なぜ一枚の層に計算をまとめるのか

完成比率の算出をあちこちに散らすと、定数を1つ変えるたびに複数箇所を直す必要が出ます。applyのような専用の関数に集めておけば、TYPE_MSやSTEP_TOTALのような定数を差し替えるだけで表示全体のつじつまが揃います。ログイン画面を自作するときも、同じように計算をひとつの層にまとめておくと後から値を調整しやすくなります。

画面別コード

「ログイン 画面 デザイン コピペ」で探して来た方は、まずこの9項目の実装から見ると早いです。実際のユーザー操作に反応するのは09同意チェックボックス(規約パネルのscrollイベント)だけで、残り8項目はrequestAnimationFrameによる固定周期(2000ms)の自動再生です。サンプルのメール・パスワードの長さ・ステップのラベル・プロバイダー数・正解マス・規約の文言は名前付きの定数で定義してあり、実際のメールアドレス・実際の電話番号・実際の認証サーバー・実際のソーシャルログインのブランドはどこにも入っていません。

01ログインカード — 入力の完成度を計算して進捗表示を満たすカード

メールとパスワードの欄が埋まった文字数をTYPE_MS70msごとに1文字ずつ増やして数え直し、emailCharsとpwCharsから完成比率を求めてカード枠のconic-gradient進捗表示(--ratio)に反映します。

ログインカード — 入力の完成度を計算して進捗表示を満たすカード
新しいタブで開く

01 · ログインカード — 入力の完成度を計算して進捗表示を満たすカード — 計測 累積面積 0.824% · 動いたフレーム 19/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

conic-gradientsetPropertyautocomplete

frameForとapplyが完成比率をconic-gradientに変える

frameForは経過ミリ秒からメール欄・パスワード欄それぞれの埋まった文字数を求め、applyがその2つの平均をリングの--ratioにそのまま渡します。

  function frameFor(t) {
    var emailChars = Math.min(Math.max(Math.floor(t / TYPE_MS), 0), EMAIL_LEN);
    var pwStart = EMAIL_LEN * TYPE_MS;
    var pwChars = t > pwStart
      ? Math.min(Math.max(Math.floor((t - pwStart) / TYPE_MS), 0), SAMPLE_PW_LEN)
      : 0;
    return { emailChars: emailChars, pwChars: pwChars };
  }

  function apply(t) {
    var f = frameFor(t);
    emailEl.value = SAMPLE_EMAIL.slice(0, f.emailChars);
    pwEl.value = PW_DOTS.slice(0, f.pwChars);
    var ratio = ((f.emailChars / EMAIL_LEN) + (f.pwChars / SAMPLE_PW_LEN)) / 2;
    ring.style.setProperty('--ratio', ratio.toFixed(4));
  }

02会員登録ステップ — 進捗率を自分で割って求めバーの長さと色を決める段階フォーム

現在のステップ番号をSTEP_TOTAL4で割って進捗率を求めつつ、STEP_MS500msのステップ内で経過した割合(0〜1)を足して毎フレーム補間し、進捗バーの長さ(--ratio)とaria-valuenowを一緒に更新します。

会員登録ステップ — 進捗率を自分で割って求めバーの長さと色を決める段階フォーム
新しいタブで開く

02 · 会員登録ステップ — 進捗率を自分で割って求めバーの長さと色を決める段階フォーム — 計測 累積面積 2.294% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

setPropertyaria-valuenowclassList

STEP_TOTAL4とSTEP_MS500がバーの補間を作る

applyはステップの境界を整数(1〜4)のままaria-valuenowに渡す一方、バーの--ratioにはステップ内の経過分を足した小数を渡すため、段階が切り替わる瞬間だけ跳ねることがありません。

  function apply(t) {
    var tt = t % CYCLE_MS;
    var idx = Math.min(Math.max(Math.floor(tt / STEP_MS), 0), STEP_TOTAL - 1);
    var current = idx + 1;
    // ステップの境界は整数(1〜4)としてそのままaria・ラベルに反映しつつ、バーそのものは
    // ステップ内の経過分(0〜1)を加えて毎フレーム少しずつ伸ばす — 計算1枚(補間)で実際の
    // 会員登録プログレスバーのように滑らかになる。
    var within = (tt % STEP_MS) / STEP_MS;
    var ratio = (idx + within) / STEP_TOTAL;
    track.style.setProperty('--ratio', ratio.toFixed(4));
    track.setAttribute('aria-valuenow', String(current));
    steps.forEach(function (el, i) {
      if (i === idx) {
        el.classList.add('is-active');
      } else {
        el.classList.remove('is-active');
      }
    });
  }

03ソーシャルログイン — ボタン数から列数を求め直し常に均等に配置するグループ

COUNTS配列 [2, 3, 4, 5, 3] の値がプロバイダーボタンの個数になり、colsForがその個数から一行に並べる列数を求め直します。ブランドロゴは使わず、仮のプロバイダー名だけを表示します。

ソーシャルログイン — ボタン数から列数を求め直し常に均等に配置するグループ
新しいタブで開く

03 · ソーシャルログイン — ボタン数から列数を求め直し常に均等に配置するグループ — 計測 累積面積 23.165% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

grid-template-columnsclip-pathclassList

colsForが個数から列数を求め直す

n<=3のときはそのまま列数にし、それを超えるとMath.ceil(n/2)で列数を求め直すため、ボタンが増減してもレイアウトが崩れません。

  function colsFor(n) {
    return n <= 3 ? n : Math.ceil(n / 2);
  }

  function apply(t) {
    var tt = t % CYCLE_MS;
    var idx = Math.min(Math.max(Math.floor(tt / STEP_MS), 0), COUNTS.length - 1);
    var n = COUNTS[idx];
    var cols = colsFor(n);
    row.style.setProperty('--cols', String(cols));
    hintCount.textContent = String(n);
    btns.forEach(function (btn, i) {
      if (i < n) {
        btn.classList.remove('is-hidden');
      } else {
        btn.classList.add('is-hidden');
      }
    });
    // 次の個数切り替えまでの残り区間を毎フレーム求め直すバー(計算1枚) —
    // グリッドの再配置が急に跳ばず、次の変化が近いことを予告する。
    var within = (tt % STEP_MS) / STEP_MS;
    tickBar.style.setProperty('--within', within.toFixed(4));
  }

04マジックリンク — 残り有効時間を求め直しリングの弧の長さで示す送信完了カード

送信後の残り有効時間の比率を毎フレーム最初から求め直し(値を積み上げません)、円周CIRCUMFERENCE276.46をVALID_SEC300秒に対する比率で逆算してstrokeDashoffsetに渡し、ドーナツ型リングを満たします。

マジックリンク — 残り有効時間を求め直しリングの弧の長さで示す送信完了カード
新しいタブで開く

04 · マジックリンク — 残り有効時間を求め直しリングの弧の長さで示す送信完了カード — 計測 累積面積 2.65% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

stroke-dasharraystrokeDashoffsetrequestAnimationFrame

VALID_SEC300とCIRCUMFERENCE276.46が弧の長さを逆算する

ratioは経過ミリ秒をCYCLE_MSで割った残り比率を返すだけで、applyがその比率を秒数表示とstrokeDashoffsetの両方に変換します。

  var VALID_SEC = 300; // バリエーション: 300 → 900
  var CIRCUMFERENCE = 276.46;
  var CYCLE_MS = 2000;

  function ratio(t) {
    return 1 - Math.min(Math.max(t / CYCLE_MS, 0), 1);
  }

  function apply(t) {
    var r = ratio(t);
    var remainingDisplaySec = Math.ceil(r * VALID_SEC);
    ring.style.strokeDashoffset = String(CIRCUMFERENCE * (1 - r));
    timeText.textContent = '残り時間' + ' ' + remainingDisplaySec + '秒';
  }

052段階認証 — 埋まった桁数の比率で確認ボタンの状態を決めるコード入力

DIGITS6桁のうち埋まっている桁数をDIGIT_MS250msごとに数え直して充填比率を求め、その比率が100%に達して初めて確認ボタンの不透明度と押せる状態が一緒に変わります。表示コードはシードを固定した疑似乱数mulberry32(42)で作った仮の値です。

2段階認証 — 埋まった桁数の比率で確認ボタンの状態を決めるコード入力
新しいタブで開く

05 · 2段階認証 — 埋まった桁数の比率で確認ボタンの状態を決めるコード入力 — 計測 累積面積 9.225% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

data-indexautocompletesetAttribute

mulberry32(42)が同じキャプチャで同じ6桁を作る

mulberry32はシード値から決定的な乱数列を作る関数で、同じシード42を渡すたびに同じ6桁の並びを返すため、キャプチャのたびに違う数字が出ることはありません。

  function mulberry32(seed) {
    return function () {
      seed |= 0; seed = (seed + 0x6D2B79F5) | 0;
      var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
      t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
      return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    };
  }

  var rand = mulberry32(42);
  var code = [];
  for (var i = 0; i < DIGITS; i++) code.push(Math.floor(rand() * 10));

06パスワード再設定リクエスト — メール形式のスコアを計算しボタン状態を決めるフォーム

入力されたメールの文字構成とアットマーク・ドメインの有無を毎フレーム調べて有効性スコアを自分で計算し、そのスコアがSCORE_THRESHOLD.9を超えて初めてリクエストボタンが有効になります。

パスワード再設定リクエスト — メール形式のスコアを計算しボタン状態を決めるフォーム
新しいタブで開く

06 · パスワード再設定リクエスト — メール形式のスコアを計算しボタン状態を決めるフォーム — 計測 累積面積 7.964% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

aria-invalidaria-describedbyclassList

アットマーク.4・ドメイン形式.4・文字数.2の配分でスコアを組み立てる

scoreはアットマークの有無に.4・ドメインらしき文字列に.4・文字数の充実度に最大.2を足し合わせるだけの単純な加点式で、3つの根拠のどれが欠けているかで採点の内訳が変わります。

  function currentText(t) {
    var n = Math.min(Math.max(Math.floor(t / TYPE_MS), 0), SAMPLE.length);
    return SAMPLE.slice(0, n);
  }

  function score(text) {
    var domain = text.split('@')[1] || '';
    var s = 0;
    if (text.indexOf('@') !== -1) s += .4;
    if (/\.[a-z]{2,}$/i.test(domain)) s += .4;
    s += Math.min(text.length / 12, 1) * .2;
    return s;
  }

07セッション期限切れ — 切れてから経過した時間で強調濃度を求め直す通知

セッションが切れた時点から経過した時間を毎フレーム求め直し、強調曲線(INTENSITY_POW1のべき乗)に乗せてバッジの背景色をpaperからerrorへチャンネルごとに補間し、再ログインボタンも同じ値で一緒に強調します。RAMP_MS1800msで強調が最大に達します。

セッション期限切れ — 切れてから経過した時間で強調濃度を求め直す通知
新しいタブで開く

07 · セッション期限切れ — 切れてから経過した時間で強調濃度を求め直す通知 — 計測 累積面積 4.845% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

getComputedStylegetPropertyValueclassList

elapsedRatioとINTENSITY_POWがバッジの色をチャンネルごとに補間する

applyはpaperとerrorそれぞれのRGB値をintensityの比率で混ぜ合わせるだけで、色相環を回すような特別な計算はしていません。

  function apply(t) {
    var ratio = elapsedRatio(t);
    var intensity = Math.pow(ratio, INTENSITY_POW);

    var r = Math.round(paperRgb[0] + (errorRgb[0] - paperRgb[0]) * intensity);
    var g = Math.round(paperRgb[1] + (errorRgb[1] - paperRgb[1]) * intensity);
    var b = Math.round(paperRgb[2] + (errorRgb[2] - paperRgb[2]) * intensity);
    badge.style.backgroundColor = 'rgb(' + r + ',' + g + ',' + b + ')';
    badge.classList.toggle('isHot', intensity > 0.5);

    btn.style.transform = 'scale(' + (1 + intensity * 0.04) + ')';

    elapsedSeconds.textContent = String(Math.round(ratio * (RAMP_MS / 1000)));
  }

08キャプチャ枠 — 選択の一致率を求め枠線の色とボタン状態を決める場所

正解マスANSWER [1, 3, 5, 7] と、時間とともに自動で選ばれるマスPICK_ORDER(わざと不正解4を含む)を毎フレーム比較して一致率を求め、MATCH_THRESHOLD1(100%一致)を超えて初めて確認ボタンが有効になります。

キャプチャ枠 — 選択の一致率を求め枠線の色とボタン状態を決める場所
新しいタブで開く

08 · キャプチャ枠 — 選択の一致率を求め枠線の色とボタン状態を決める場所 — 計測 累積面積 4.765% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

data-indexgrid-template-columnsclassList

selectedSoFarとmatchRatioOfが一致率を求め直す

selectedSoFarは経過ミリ秒をPICK_MS350msで割って何個目まで選ばれたかを求め、matchRatioOfがその選択と正解の重なりを数えて一致率を返します。

  function selectedSoFar(t) {
    var count = clamp(Math.floor(t / PICK_MS), 0, PICK_ORDER.length);
    return PICK_ORDER.slice(0, count);
  }

  function matchRatioOf(selected) {
    var hit = 0;
    for (var i = 0; i < ANSWER.length; i++) {
      if (selected.indexOf(ANSWER[i]) !== -1) hit += 1;
    }
    return clamp(hit / ANSWER.length, 0, 1);
  }

  function apply(t) {
    var selected = selectedSoFar(t);
    tiles.forEach(function (tile) {
      var idx = Number(tile.getAttribute('data-index'));
      tile.classList.toggle('isPicked', selected.indexOf(idx) !== -1);
    });
    var ratio = matchRatioOf(selected);
    matchPercent.textContent = String(Math.round(ratio * 100));
    submitBtn.disabled = ratio < MATCH_THRESHOLD;
  }

09同意チェックボックス — 規約のスクロール進捗を測り有効化の時点を決めるUI

規約パネルの本物のscrollイベントで進捗を求め、SCROLL_THRESHOLD.7を超えるとチェックボックスを有効にします。9項目の中でこの項目だけが実際のユーザーのスクロール入力に反応するリスナーを持ち、自動再生もpanel.scrollTopを毎フレーム直接代入することで同じ本物のscrollイベントを発生させ、1つのonScroll経路をそのまま通します。

同意チェックボックス — 規約のスクロール進捗を測り有効化の時点を決めるUI
新しいタブで開く

09 · 同意チェックボックス — 規約のスクロール進捗を測り有効化の時点を決めるUI — 計測 累積面積 15.528% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scrollTopaddEventListenersetAttribute

onScrollが進捗をmaxScrollとの比率で求め直す

onScrollはスクロール可能な最大量maxScrollに対する現在位置の比率をprogressとして求め、その値がSCROLL_THRESHOLDをまたぐ瞬間だけチェックボックスの有効・無効とラベルの文言を切り替えます。

  function onScroll() {
    var maxScroll = panel.scrollHeight - panel.clientHeight;
    var progress = maxScroll > 0 ? clamp(panel.scrollTop / maxScroll, 0, 1) : 1;
    if (progress >= SCROLL_THRESHOLD && !unlocked) {
      unlocked = true;
      checkbox.disabled = false;
      checkbox.setAttribute('aria-disabled', 'false');
      label.textContent = DONE_TEXT;
    } else if (progress < SCROLL_THRESHOLD && unlocked) {
      unlocked = false;
      checkbox.disabled = true;
      checkbox.checked = false;
      checkbox.setAttribute('aria-disabled', 'true');
      label.textContent = lockedText;
    }
  }

どこで使う画面か — ログイン・会員登録フォームを作り直すチーム

ログインフォームを作り直すチームがこの9つの画面を探す理由は、たいてい一つです。01 のように入力の完成度を進捗表示で見せるカードを付けたいけれど、その進捗率を何を基準に計算すればいいのか分からないからです。

パスワード再設定画面を担当する人は、06 のようにメール形式だけを見てボタンの状態を先に決めるかどうかの判断によく直面します。形式チェックと実際の送信成功をどう見分けて表示するかが次の問題になります。

利用規約の同意画面を作る人にとっては、09 のスクロール進捗の計算がそのまま画面の全てです。スクロールを最後まで進めないと同意ボタンが有効にならない条件を、どの時点で判定するかによってユーザーの感じる窮屈さが変わります。

何から先に壊れるか — 空のメール・期限直前・正解マス0個

メールアドレスが空のとき、scoreの判定はどう動くか

06のscoreはtextが空文字列でもdomainが空文字列になるだけで例外は起きません。アットマークがないのでsに.4は足されず、正規表現も一致せずもう.4は足されず、文字数0で最後の.2も0のままなのでscore('')は必ず0を返します。安全に失敗する設計ですが、SCORE_THRESHOLD.9に届くにはアットマークとドメイン形式の両方に加えて文字数もある程度必要で、片方だけでは絶対にボタンが開かないことを覚えておく必要があります。

有効時間が切れる直前、残り秒表示とリングの見た目がずれて見えるとき

04のremainingDisplaySecはMath.ceil(r * VALID_SEC)で秒数を切り上げて求めるため、リングの弧がほぼ0まで縮んでいても表示上は「残り1秒」のまま止まって見える瞬間があります。リング自体はrをそのまま使うので途切れなく縮み続けますが、切り上げた整数の秒数表示だけが実際の弧より少し遅れて0になるということです。

正解マスが0個のとき、確認ボタンが常に有効になってしまうとき

08のmatchRatioOfはhit / ANSWER.lengthで一致率を求めるため、ANSWER配列が将来空になると分母が0になり計算結果はNaNになります。submitBtn.disabled = ratio < MATCH_THRESHOLDという判定はNaN < 1が常にfalseを返すため、disabledにfalseが入り続け、正解マスを1つも選ばなくても確認ボタンが有効なままになってしまいます。ANSWERは名前付き定数として空でないことを前提に置いているためで、呼び出す前に配列の長さを確かめる一行を足しておくと安全です。

reduced-motionでどう変わるか、3つのバリエーション

紙のポスターに認証画面を並べるのと違い、この9種は同じ見た目のまま定数を1つ変えるだけで体験の重さが変わります。9種の実装はydncqy9nで開くzipにまとめてあり、vanillaフォルダには9項目それぞれのindex.htmlが、reactフォルダには同じ計算をコンポーネント化したtsxが入っています。

名前 値 感じ方
マジックリンクの有効時間 04 VALID_SEC 300 → 900 300秒(5分)にするとリングが速く縮んで急かされる感じになるがセキュリティ面では有利で、900秒(15分)に伸ばすと余裕はできるがリングがほとんど縮まないように見えてリンクが生きている感覚が薄れる
セッション期限切れの強調曲線 07 INTENSITY_POW 1 → 3 指数を1にすると経過時間に比例してバッジの色が緩やかに濃くなり切迫感が薄く、3に上げると序盤はほとんど変わらず終盤で急に濃くなり長く放置したという警告が強く出る
同意チェックボックスのスクロール閾値 09 SCROLL_THRESHOLD .7 → 1 閾値を.7にすると規約を70%読むだけでチェックボックスが開き登録は速くなるが読み終えた保証は弱く、1にすると最後までスクロールしないと開かず確実だが長い規約では退屈に感じられることがある

prefers-reduced-motionをどう扱うか

9項目ともwindow.matchMedia('(prefers-reduced-motion: reduce)')の結果をreduceという変数に持ち、真であれば自動再生を回すrAFループ自体を掛けず、決まった一地点の状態だけを計算して固定します。05の2段階認証はアニメーションが切れると六桁が埋まっていく過程なしに、空欄のまま確認ボタンが無効化された初期状態で固定され、09の同意チェックボックスは自動スクロールのシミュレーションだけを止め、実際のスクロールに応じた進捗計算はそのまま動作します。aria-valuenow・aria-invalid・aria-disabledのような属性はアニメーションの有無に関わらず常に実際の状態を反映します。

コピー&ペーストで動く理由

9項目とも定数・関数・クラス名を1つのindex.htmlの中だけで完結させ、外部のライブラリやCDNは一切呼び出しません。フォルダを開いてブラウザに読み込ませるだけで、追加のビルド手順なしに同じ計算がそのまま動き出します。ログイン画面を自作する際も、名前付き定数の値だけ差し替えれば同じ計算の層をそのまま流用できます。

数値が画面を変える場所 — 完成度・残り時間・既読比率

完成比率がカードの進捗リングを動かす

01ログインカードでは、メール欄とパスワード欄それぞれの埋まった文字数という2つの整数が、平均された1つの小数ratioに変わり、そのratioがconic-gradientの--ratioという1つのCSS変数だけを動かします。数字を直接色に書くのではなく、比率という中間値を経由している点がポイントです。

残り有効時間が円周の弧の長さを動かす

04マジックリンクではVALID_SECという外の数字がratioという比率に変わり、その比率がCIRCUMFERENCEという円周の長さと掛け合わされてstrokeDashoffsetというSVGの座標を動かします。値を積み上げず毎フレーム最初から求め直すため、タブを閉じて開き直しても表示がずれません。

スクロール進捗が同意チェックの解錠を動かす

09同意チェックボックスではpanel.scrollTopという実測されたピクセル数がmaxScrollとの比率に変わり、その比率がSCROLL_THRESHOLDをまたいだ瞬間だけチェックボックスの有効・無効という2値の状態を切り替えます。連続した比率が離散した状態を1回だけ変える、という関係です。

ログインフォームを組み込む前によくある質問

2段階認証のコードは毎回同じ数字ですか

はい。mulberry32(42)というシードを固定した疑似乱数関数で作っているため、同じシード値からは毎回同じ6桁の並びが求まります。Math.random()は使っておらず、キャプチャのたびに違う数字が出ることはありません。

ソーシャルログインのボタンは実際のGoogleやAppleでログインできますか

いいえ。プロバイダーボタンはすべて仮の名前(「Provider A」のような表記)で、実際のOAuth・実際のソーシャルログインのブランド・実キーはどこにもありません。ボタンの個数から列数を求め直すレイアウト計算だけを見せるための仮の画面です。

ログイン 画面 デザイン テンプレートとしてそのまま使えますか

はい。9項目とも名前付き定数(サンプルのメール・パスワードの長さ・ステップのラベル・プロバイダー数・正解マス・規約の文言)をindex.htmlの先頭にまとめてあるので、その値だけ差し替えれば自分のログイン・会員登録フローのテンプレートとしてそのまま使えます。

もっと読む

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。