c18389e80430

drop onboarding rune opacity 60% and rework new vault screen to share onboarding card visual language (v2.8.1)

time
2026-04-28 04:51 UTC · 6 months ago
machine
koh2
parent
80afc813ba15
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/styles/popup.css+146 −0src/popup/views/Lock.tsx+96 −92src/popup/views/Onboarding.tsx+1 −1
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.8.0",4+  "version": "2.8.1",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.8.0",4+  "version": "2.8.1",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "2.8.0",3+  "version": "2.8.1",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/popup/styles/popup.css+146 −0
@@ -5615,3 +5615,149 @@56155615   from { opacity: 1; transform: translateX(0); }56165616   to   { opacity: 0; transform: translateX(-12px); }56175617 }5618+5619+/* ─── New vault screen — matched to onboarding card language ─────────── */5620+5621+.peach-newvault {5622+  position: relative;5623+  display: flex;5624+  flex-direction: column;5625+  gap: 12px;5626+  padding: 16px 16px 18px;5627+  overflow: hidden;5628+}5629+5630+/* Suppress the warm-blob ambient art inherited from .lock-screen — the5631+   new vault card now carries the visual weight. */5632+.peach-newvault::before {5633+  content: none;5634+}5635+5636+.peach-newvault-header {5637+  position: relative;5638+  z-index: 2;5639+  display: flex;5640+  align-items: center;5641+  margin-bottom: 2px;5642+}5643+5644+.peach-newvault-mark {5645+  position: absolute;5646+  right: -12px;5647+  top: 6px;5648+  width: 110px;5649+  height: 110px;5650+  pointer-events: none;5651+  opacity: 0.10;5652+  filter: saturate(0.9);5653+  z-index: 0;5654+}5655+.peach-newvault-mark-art {5656+  width: 100%;5657+  height: 100%;5658+}5659+.peach-newvault-mark-art path:nth-child(2),5660+.peach-newvault-mark-art path:nth-child(3) {5661+  stroke: rgba(123, 144, 96, 0.88);5662+  fill: rgba(123, 144, 96, 0.88);5663+}5664+5665+.peach-newvault-card {5666+  position: relative;5667+  z-index: 1;5668+  display: flex;5669+  flex-direction: column;5670+  gap: 12px;5671+  padding: 22px 20px 18px;5672+  border-radius: 16px;5673+  border: 1px solid var(--peach-t3);5674+  background:5675+    linear-gradient(180deg, rgba(240, 232, 216, 0.03), rgba(240, 232, 216, 0.015));5676+  box-shadow:5677+    inset 0 1px 0 rgba(240, 232, 216, 0.04),5678+    0 12px 28px rgba(0, 0, 0, 0.12);5679+  animation: peach-onboard-enter 320ms cubic-bezier(0.16, 1, 0.3, 1) both;5680+}5681+5682+.peach-newvault-headline {5683+  margin: 0;5684+  font-family: var(--font-display);5685+  font-style: italic;5686+  font-weight: 400;5687+  font-size: 24px;5688+  line-height: 1.05;5689+  color: var(--peach-t0);5690+  white-space: pre-line;5691+}5692+5693+.peach-newvault-divider {5694+  margin: 2px 0 4px;5695+  border: 0;5696+  border-top: 1px solid var(--peach-t3);5697+  width: 100%;5698+}5699+5700+.peach-newvault-field {5701+  display: flex;5702+  flex-direction: column;5703+  gap: 4px;5704+}5705+5706+.peach-newvault-label-row {5707+  display: flex;5708+  align-items: center;5709+  justify-content: space-between;5710+}5711+5712+/* Monospace input scaled to the card. Replaces the 22px unlock-input5713+   so the placeholder no longer overflows and the field reads as part5714+   of the same surface as the body copy. */5715+.peach-newvault-input {5716+  font-size: 14px;5717+  letter-spacing: 0.18em;5718+  padding: 8px 0;5719+}5720+.peach-newvault-input::placeholder {5721+  letter-spacing: 0.18em;5722+  color: var(--peach-t2);5723+}5724+5725+.peach-newvault-hint {5726+  margin: 2px 0 0;5727+  font-family: "IBM Plex Mono", monospace;5728+  font-size: 10px;5729+  color: var(--peach-t2);5730+  letter-spacing: 0.04em;5731+}5732+5733+.peach-newvault-submit {5734+  margin-top: 4px;5735+}5736+5737+.peach-newvault-warning {5738+  display: flex;5739+  align-items: flex-start;5740+  gap: 8px;5741+  margin: 4px 0 0;5742+  padding-top: 10px;5743+  border-top: 1px dashed var(--peach-t3);5744+  font-size: 12px;5745+  line-height: 1.5;5746+  color: var(--peach-t1);5747+}5748+5749+.peach-newvault-warning-icon {5750+  color: var(--peach-accent);5751+  font-size: 13px;5752+  line-height: 1.4;5753+  flex: 0 0 auto;5754+}5755+5756+.peach-newvault-footer {5757+  position: relative;5758+  z-index: 1;5759+  display: flex;5760+  flex-direction: column;5761+  gap: 4px;5762+  padding: 4px 4px 0;5763+}

view file

src/popup/views/Lock.tsx+96 −92
@@ -238,10 +238,10 @@238238       {/* ── Onboarding (hasVault = false) ─────────────────────────── */}239239       <Match when={true}>240240         <section241-          class="peach-screen peach-theme lock-screen lock-screen--vault-stage lock-screen--onboarding"241+          class="peach-screen peach-theme lock-screen lock-screen--vault-stage peach-newvault"242242           data-motion-phase="enter"243243         >244-          <header class="peach-screen-header lock-screen-header">244+          <header class="peach-screen-header peach-newvault-header">245245             <div class="peach-wordmark-stack">246246               <PeachWordmark class="peach-wordmark-wrap" />247247               <Show when={props.isPro}>@@ -250,116 +250,120 @@250250             </div>251251           </header>252252 253-          <div class="peach-lock-hero">254-            <div class="peach-lock-hero-art" aria-hidden="true">255-              <PeachMark class="peach-lock-hero-mark" />256-            </div>257-            <span class="peach-lock-eyebrow">new vault</span>258-            <h1 class="peach-onboarding-headline">259-              keep your260-              <br />261-              secrets.262-            </h1>253+          <div class="peach-newvault-mark" aria-hidden="true">254+            <PeachMark class="peach-newvault-mark-art" />263255           </div>264256 265-          <section class="peach-lock-panel peach-lock-panel--primary">266-            <div class="peach-lock-label-row">267-              <label class="peach-label" for="master-password">268-                master password269-              </label>270-              <button271-                class="peach-lock-reveal-btn"272-                type="button"273-                onClick={() => setShowPassword((value) => !value)}274-                aria-label={showPassword() ? "Hide password" : "Show password"}275-              >276-                <Show277-                  when={showPassword()}278-                  fallback={257+          <div class="peach-newvault-card">258+            <h1 class="peach-newvault-headline">{"keep your\nsecrets."}</h1>259+            <hr class="peach-newvault-divider" />260+261+            <div class="peach-newvault-field">262+              <div class="peach-newvault-label-row">263+                <label class="peach-label" for="master-password">264+                  master password265+                </label>266+                <button267+                  class="peach-lock-reveal-btn"268+                  type="button"269+                  onClick={() => setShowPassword((value) => !value)}270+                  aria-label={showPassword() ? "Hide password" : "Show password"}271+                >272+                  <Show273+                    when={showPassword()}274+                    fallback={275+                      <svg width="15" height="10" viewBox="0 0 14 10" fill="none" stroke="currentColor" stroke-width="0.9" stroke-linecap="round">276+                        <path d="M1 5C1 5 2.8 1 7 1C11.2 1 13 5 13 5C13 5 11.2 9 7 9C2.8 9 1 5 1 5Z" />277+                        <circle cx="7" cy="5" r="1.75" />278+                      </svg>279+                    }280+                  >279281                     <svg width="15" height="10" viewBox="0 0 14 10" fill="none" stroke="currentColor" stroke-width="0.9" stroke-linecap="round">280-                      <path d="M1 5C1 5 2.8 1 7 1C11.2 1 13 5 13 5C13 5 11.2 9 7 9C2.8 9 1 5 1 5Z" />281-                      <circle cx="7" cy="5" r="1.75" />282+                      <path d="M1 2C3 4.5 5 6 7 6C9 6 11 4.5 13 2" />283+                      <path d="M3.5 5.5L2.5 7.5" />284+                      <path d="M7 6.5V8.5" />285+                      <path d="M10.5 5.5L11.5 7.5" />282286                     </svg>283-                  }284-                >285-                  <svg width="15" height="10" viewBox="0 0 14 10" fill="none" stroke="currentColor" stroke-width="0.9" stroke-linecap="round">286-                    <path d="M1 2C3 4.5 5 6 7 6C9 6 11 4.5 13 2" />287-                    <path d="M3.5 5.5L2.5 7.5" />288-                    <path d="M7 6.5V8.5" />289-                    <path d="M10.5 5.5L11.5 7.5" />290-                  </svg>291-                </Show>292-                {showPassword() ? "hide" : "show"}293-              </button>287+                  </Show>288+                  {showPassword() ? "hide" : "show"}289+                </button>290+              </div>291+              <input292+                id="master-password"293+                class="peach-underline-input peach-newvault-input lock-password-input"294+                type={showPassword() ? "text" : "password"}295+                placeholder="••••••••••••"296+                autocomplete="new-password"297+                value={password()}298+                disabled={!props.ready}299+                onInput={(event) => setPassword((event.target as HTMLInputElement).value)}300+                onKeyDown={(event) => {301+                  if (event.key !== "Enter" || !props.ready) return;302+                  void handleCreate();303+                }}304+              />305+              <Show306+                when={password().length > 0 && passwordStrength() !== null}307+                fallback={308+                  <p class="peach-newvault-hint">at least 12 characters</p>309+                }310+              >311+                <div class="peach-password-strength">312+                  <div class={`peach-strength-bar peach-strength-bar--${passwordStrength()}`}>313+                    <div class="peach-strength-fill"></div>314+                  </div>315+                  <span class="peach-strength-label">{passwordStrength()}</span>316+                </div>317+              </Show>294318             </div>295-            <input296-              id="master-password"297-              class="peach-underline-input peach-unlock-input lock-password-input"298-              type={showPassword() ? "text" : "password"}299-              placeholder="at least 12 characters"300-              autocomplete="new-password"301-              value={password()}302-              disabled={!props.ready}303-              onInput={(event) => setPassword((event.target as HTMLInputElement).value)}304-              onKeyDown={(event) => {305-                if (event.key !== "Enter" || !props.ready) return;306-                void handleCreate();307-              }}308-            />309319 310-            <Show when={password().length > 0 && passwordStrength() !== null}>311-              <div class="peach-password-strength">312-                <div class={`peach-strength-bar peach-strength-bar--${passwordStrength()}`}>313-                  <div class="peach-strength-fill"></div>314-                </div>315-                <span class="peach-strength-label">{passwordStrength()}</span>316-              </div>317-            </Show>320+            <div class="peach-newvault-field">321+              <label class="peach-label" for="confirm-password">322+                confirm password323+              </label>324+              <input325+                id="confirm-password"326+                class="peach-underline-input peach-newvault-input"327+                type={showPassword() ? "text" : "password"}328+                placeholder="••••••••••••"329+                autocomplete="new-password"330+                value={confirmPassword()}331+                disabled={!props.ready}332+                onInput={(event) => setConfirmPassword((event.target as HTMLInputElement).value)}333+                onKeyDown={(event) => {334+                  if (event.key === "Enter" && props.ready) void handleCreate();335+                }}336+              />337+            </div>318338 319-            <label class="peach-label" for="confirm-password">320-              confirm password321-            </label>322-            <input323-              id="confirm-password"324-              class="peach-underline-input peach-unlock-input"325-              type={showPassword() ? "text" : "password"}326-              placeholder="••••••••••••"327-              autocomplete="new-password"328-              value={confirmPassword()}329-              disabled={!props.ready}330-              onInput={(event) => setConfirmPassword((event.target as HTMLInputElement).value)}331-              onKeyDown={(event) => {332-                if (event.key === "Enter" && props.ready) void handleCreate();333-              }}334-            />335-336339             <Show when={error()}>337340               <p class="peach-hint peach-hint-error">{error()}</p>338341             </Show>339342 340343             <button341-              class="peach-btn peach-btn--accent peach-btn--md peach-lock-submit"344+              class="peach-btn peach-btn--accent peach-btn--md peach-lock-submit peach-newvault-submit"342345               onClick={() => void handleCreate()}343346               disabled={!props.ready || loading() || !canCreate()}344347             >345348               {loading() ? "creating..." : "create vault"}346349             </button>347350 348-            <p class="peach-hint peach-hint-error peach-lock-warning">349-              If you lose this password, Peach cannot recover your vault. There is no reset.351+            <p class="peach-newvault-warning">352+              <span class="peach-newvault-warning-icon" aria-hidden="true">⚠</span>353+              <span>If you forget it, your vault cannot be opened. There is no reset.</span>350354             </p>355+          </div>351356 352-            <div class="peach-lock-panel-footer">353-              <p class="peach-hint">Already have a backup?</p>354-              <button355-                class="peach-lock-restore-link"356-                onClick={() => openRestore("restore")}357-                disabled={!props.ready}358-              >359-                › restore from backup360-              </button>361-            </div>362-          </section>357+          <div class="peach-newvault-footer">358+            <p class="peach-hint">Already have a backup?</p>359+            <button360+              class="peach-lock-restore-link"361+              onClick={() => openRestore("restore")}362+              disabled={!props.ready}363+            >364+              › restore from backup365+            </button>366+          </div>363367         </section>364368       </Match>365369     </Switch>

view file

src/popup/views/Onboarding.tsx+1 −1
@@ -75,7 +75,7 @@7575       class="peach-screen peach-theme peach-onboarding"7676       classList={{ "is-completing": completing() }}7777     >78-      <RuneBackground opacity={0.07} />78+      <RuneBackground opacity={0.028} />7979 8080       <header class="peach-screen-header peach-onboarding-header">8181         <PeachWordmark class="peach-wordmark-wrap" />

view file