Skip to content

Commit 781fb4a

Browse files
samejrclaude
andcommitted
feat(webapp): move the profile name and email onto the row, behind an edit icon
The Full name and Email address rows put their current value in description text under the label. It now sits as normal text on the right of the row, just left of the action, so the label and the value read as one line and the rows match the shape of the switch and select rows around them. The action itself is now a square icon-only button carrying a pencil glyph rather than the words Update and Edit. The small-icon variant is a fixed 34px wide so a row of icon buttons with different glyph ratios lines up; here there is only one, so it's overridden to a true square against its own height. Inside the modals: the name dialog is titled Full name and its primary button now says Update rather than Edit, and both dialogs drop the label above the input, which only repeated the title a few pixels below it. The inputs carry an aria-label instead so they still have an accessible name. The new icon strokes in currentColor rather than white, so it follows the theme and the button's hover state - a hard-coded white glyph would have all but vanished on Light and White. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 6682a3d commit 781fb4a

2 files changed

Lines changed: 54 additions & 20 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/** Pencil over a couple of text lines — editing a value in place. */
2+
export function EditPencilIcon({ className }: { className?: string }) {
3+
return (
4+
<svg
5+
className={className}
6+
width="24"
7+
height="24"
8+
viewBox="0 0 24 24"
9+
fill="none"
10+
xmlns="http://www.w3.org/2000/svg"
11+
>
12+
<path
13+
d="M18.7573 3.6275L20.3732 5.24335C21.1542 6.0244 21.1542 7.29073 20.3732 8.07178L9.72032 18.7246C9.57777 18.8671 9.3957 18.9631 9.19759 19.0002L4.03377 19.9669L5.00052 14.8031C5.03765 14.6051 5.1336 14.4229 5.27604 14.2804L15.9289 3.6275C16.71 2.84645 17.9763 2.84645 18.7573 3.6275Z"
14+
stroke="currentColor"
15+
strokeWidth="2"
16+
/>
17+
<line x1="17.6464" y1="10.3536" x2="13.6464" y2="6.35355" stroke="currentColor" />
18+
<path d="M13 21L21 21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
19+
<path d="M18 17L21 17" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
20+
</svg>
21+
);
22+
}

apps/webapp/app/routes/account._index/route.tsx

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from "react";
22
import { useFetcher, useLoaderData } from "@remix-run/react";
33
import { type ActionFunction, json, type LoaderFunctionArgs } from "@remix-run/server-runtime";
44
import { z } from "zod";
5+
import { EditPencilIcon } from "~/assets/icons/EditPencilIcon";
56
import { UserProfilePhoto } from "~/components/UserProfilePhoto";
67
import {
78
MainHorizontallyCenteredContainer,
@@ -484,25 +485,33 @@ function EditNameButton() {
484485
}}
485486
>
486487
<DialogTrigger asChild>
487-
<Button variant="secondary/small">Update</Button>
488+
<Button
489+
type="button"
490+
variant="secondary/small-icon"
491+
className="w-6 min-w-0 px-0"
492+
LeadingIcon={<EditPencilIcon className="size-3.5" />}
493+
aria-label="Edit your name"
494+
/>
488495
</DialogTrigger>
489496
{/* Mounted only while open so the field re-seeds from the stored name each
490497
time: a `defaultValue` on a field that stays mounted keeps whatever was
491498
typed and abandoned last time. */}
492499
{isOpen && (
493500
<DialogContent className="sm:max-w-md">
494501
<DialogHeader>
495-
<DialogTitle>Edit your name</DialogTitle>
502+
<DialogTitle>Full name</DialogTitle>
496503
</DialogHeader>
497504
<fetcher.Form method="post">
498505
<input type="hidden" name="action" value="update-name" />
499506
<div className="py-4">
507+
{/* The dialog title already names the field, so the input carries an
508+
aria-label rather than a visible one that would repeat it. */}
500509
<InputGroup fullWidth>
501-
<Label htmlFor="profile-name">Full name</Label>
502510
<Input
503511
id="profile-name"
504512
name="name"
505513
type="text"
514+
aria-label="Full name"
506515
maxLength={MAX_NAME_LENGTH}
507516
placeholder="Your full name"
508517
defaultValue={user.name ?? ""}
@@ -517,7 +526,7 @@ function EditNameButton() {
517526
Cancel
518527
</Button>
519528
<Button type="submit" variant="primary/medium" disabled={isSubmitting}>
520-
{isSubmitting ? "Saving…" : "Edit"}
529+
{isSubmitting ? "Saving…" : "Update"}
521530
</Button>
522531
</DialogFooter>
523532
</fetcher.Form>
@@ -552,7 +561,13 @@ function EditEmailButton({ isSsoManaged }: { isSsoManaged: boolean }) {
552561
}}
553562
>
554563
<DialogTrigger asChild>
555-
<Button variant="secondary/small">Edit</Button>
564+
<Button
565+
type="button"
566+
variant="secondary/small-icon"
567+
className="w-6 min-w-0 px-0"
568+
LeadingIcon={<EditPencilIcon className="size-3.5" />}
569+
aria-label="Edit your email address"
570+
/>
556571
</DialogTrigger>
557572
{/* Mounted only while open, so the field re-seeds from the stored email */}
558573
{isOpen && (
@@ -570,12 +585,13 @@ function EditEmailButton({ isSsoManaged }: { isSsoManaged: boolean }) {
570585
<fetcher.Form method="post">
571586
<input type="hidden" name="action" value="update-email" />
572587
<div className="py-4">
588+
{/* Labelled by the dialog title, as with the name field above. */}
573589
<InputGroup fullWidth>
574-
<Label htmlFor="profile-email">Update your email address</Label>
575590
<Input
576591
id="profile-email"
577592
name="email"
578593
type="email"
594+
aria-label="Email address"
579595
maxLength={MAX_EMAIL_LENGTH}
580596
placeholder="Your email address"
581597
defaultValue={user.email}
@@ -907,28 +923,24 @@ export default function Page() {
907923
</div>
908924
<div className="flex min-h-16 w-full items-center border-b border-grid-dimmed">
909925
<div className="flex w-full items-center justify-between gap-4">
910-
<div className={cn("flex-1", SETTINGS_ROW_TITLE_GAP)}>
911-
<Label>Full name</Label>
912-
<SettingsRowDescription className="break-words">
926+
<Label>Full name</Label>
927+
<div className="flex min-w-0 items-center gap-3">
928+
<Paragraph variant="small" className="min-w-0 break-words text-right">
913929
{user.name ?? "Not set"}
914-
</SettingsRowDescription>
915-
</div>
916-
<div className="flex flex-none items-center">
930+
</Paragraph>
917931
<EditNameButton />
918932
</div>
919933
</div>
920934
</div>
921935
<div className="flex min-h-16 w-full items-center border-b border-grid-dimmed">
922936
<div className="flex w-full items-center justify-between gap-4">
923-
<div className={cn("flex-1", SETTINGS_ROW_TITLE_GAP)}>
924-
<Label>Email address</Label>
925-
{/* break-words: an address has no spaces to wrap at, so a long
926-
one would otherwise run under the button */}
927-
<SettingsRowDescription className="break-words">
937+
<Label>Email address</Label>
938+
<div className="flex min-w-0 items-center gap-3">
939+
{/* break-all: an address has no spaces to wrap at, so a long one
940+
would otherwise push the button off the row */}
941+
<Paragraph variant="small" className="min-w-0 break-all text-right">
928942
{user.email}
929-
</SettingsRowDescription>
930-
</div>
931-
<div className="flex flex-none items-center">
943+
</Paragraph>
932944
<EditEmailButton isSsoManaged={isSsoManaged} />
933945
</div>
934946
</div>

0 commit comments

Comments
 (0)