Skip to content

Commit eb227b4

Browse files
committed
feat(ui): redesign Edit/Add Account provider picker for legibility in dark mode
The previous 2-column grid forced tiles into a 1:1 aspect ratio which squeezed the provider name into a narrow column — "Microsoft OAuth" and "Yahoo Mail OAuth" were barely readable, and the icons rendered with a 20%-opacity wash that almost vanished against the admin panel's dark modal background. Redesign focuses on legibility and information density: - 3-column grid with responsive collapse (→ 2 cols <960px, 1 col <600px). The max-width is lifted from 800px → 960px so each tile gets real breathing room. - Tiles are landscape now, not portrait. A 170px min-height holds the icon (48px), title, and a new one-line tagline — no more squished text. - Every provider gets a tagline explaining what it actually is. Users previously had to click a tile to discover whether it meant "Google Workspace" or "Google Account": the tagline now says so directly. Example lines: Gmail · Google Workspace & personal accounts · 1-click OAuth Microsoft 365 · Outlook · Exchange · Azure AD · 1-click OAuth SMTP · Any SMTP server · full control over host, port & auth SendGrid · Transactional API · high deliverability · analytics Mailgun · Developer-first API · EU/US regions · routes & webhooks - Providers are grouped under two category headers: "OAuth Providers" (badged "Recommended") "Custom & Transactional APIs" The header is a small caps label with a hairline divider — matches the admin-panel content-manager layout language, no dissonance. - Icon backgrounds switched from e.g. "#4285F433" (20% opacity) to "rgba(66, 133, 244, 0.22)" with brighter foreground colours for the letter/icon itself. That keeps the brand hue but stays visible on both light and dark surfaces. - Selection state now has a soft green ring (box-shadow) on top of the green border, so the active card reads clearly without being visually loud. The success check badge is styled with a matching drop-shadow. - Accessibility: every tile is now role="button", tabIndex=0, and responds to Enter / Space, so keyboard users can actually navigate the picker. Focus-visible outline is in the plugin's primary colour. No logic or API change — provider ids, formData shape and the rest of the wizard (steps 2-4) are untouched.
1 parent f6d92d0 commit eb227b4

1 file changed

Lines changed: 204 additions & 74 deletions

File tree

admin/src/components/AddAccountModal.jsx

Lines changed: 204 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -181,100 +181,173 @@ const StepLabel = styled(Typography)`
181181
transition: all 0.3s ease;
182182
`;
183183

184+
const ProviderCategoryHeader = styled(Box)`
185+
display: flex;
186+
align-items: center;
187+
gap: 12px;
188+
margin: 0 0 16px;
189+
animation: ${fadeIn} 0.4s ease;
190+
191+
&::after {
192+
content: '';
193+
flex: 1;
194+
height: 1px;
195+
background: linear-gradient(90deg, ${colors.border}, transparent);
196+
}
197+
`;
198+
199+
const ProviderCategoryLabel = styled(Typography)`
200+
font-size: 12px;
201+
font-weight: 600;
202+
letter-spacing: 0.08em;
203+
text-transform: uppercase;
204+
color: ${colors.textLight};
205+
`;
206+
207+
const ProviderCategoryHint = styled.span`
208+
font-size: 11px;
209+
font-weight: 500;
210+
padding: 3px 10px;
211+
border-radius: 999px;
212+
background: ${colors.primaryLight};
213+
color: ${colors.primary};
214+
letter-spacing: 0.02em;
215+
text-transform: none;
216+
`;
217+
184218
const ProvidersGrid = styled(Box)`
185219
display: grid;
186-
grid-template-columns: repeat(2, 1fr);
187-
gap: 20px;
188-
margin-bottom: 24px;
220+
grid-template-columns: repeat(3, 1fr);
221+
gap: 16px;
222+
margin: 0 auto 28px;
189223
animation: ${slideIn} 0.5s ease;
190-
max-width: 800px;
191-
margin-left: auto;
192-
margin-right: auto;
224+
max-width: 960px;
225+
226+
@media (max-width: 960px) {
227+
grid-template-columns: repeat(2, 1fr);
228+
}
229+
@media (max-width: 600px) {
230+
grid-template-columns: 1fr;
231+
}
193232
`;
194233

234+
/**
235+
* Redesigned provider tile.
236+
*
237+
* Portrait aspect-ratio was dropped — the old 1:1 tiles forced the
238+
* provider name into a narrow column so "Microsoft OAuth" and
239+
* "Yahoo Mail OAuth" were getting squeezed. The new layout is a
240+
* breathable landscape card with:
241+
* - a prominent icon row (68px),
242+
* - a bold, high-contrast title,
243+
* - a one-line tagline that explains what the provider actually is,
244+
* - a clearly visible selection ring with success-green check badge.
245+
*
246+
* Colours are taken from the plugin palette and dark-mode-safe: the
247+
* card background falls back to a transparent wash that sits cleanly
248+
* on top of the admin panel surface in both light and dark themes.
249+
*/
195250
const ProviderCard = styled(Box)`
196-
background: ${props => props.$selected ? colors.successLight : props.theme.colors.neutral0};
197-
border: 2px solid ${props => props.$selected ? colors.success : colors.border};
198-
border-radius: 12px;
199-
padding: 24px;
251+
background: ${props => props.$selected
252+
? `linear-gradient(180deg, ${colors.successLight}, rgba(92, 177, 118, 0.04))`
253+
: 'var(--colors-neutral100, rgba(255,255,255,0.02))'};
254+
border: 1.5px solid ${props => props.$selected ? colors.success : colors.border};
255+
border-radius: 14px;
256+
padding: 22px 20px 20px;
200257
cursor: pointer;
201-
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
202-
text-align: center;
203-
aspect-ratio: 1;
204-
min-height: 180px;
258+
transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s, border-color 0.2s, background 0.2s;
205259
display: flex;
206260
flex-direction: column;
207-
align-items: center;
208-
justify-content: center;
209-
gap: 12px;
261+
align-items: flex-start;
262+
gap: 14px;
210263
position: relative;
211264
overflow: hidden;
212-
265+
min-height: 170px;
266+
213267
&::before {
214268
content: '';
215269
position: absolute;
216-
top: 0;
217-
left: 0;
218-
right: 0;
219-
bottom: 0;
220-
background: linear-gradient(135deg, transparent, rgba(73, 69, 255, 0.05));
270+
inset: 0;
271+
background: linear-gradient(135deg, transparent 60%, rgba(73, 69, 255, 0.06));
221272
opacity: 0;
222-
transition: opacity 0.3s;
273+
transition: opacity 0.25s;
274+
pointer-events: none;
223275
}
224-
276+
225277
&:hover {
226-
transform: translateY(-4px);
227-
box-shadow: 0 8px 24px rgba(73, 69, 255, 0.12);
278+
transform: translateY(-3px);
279+
box-shadow: 0 10px 28px rgba(73, 69, 255, 0.18);
228280
border-color: ${props => props.$selected ? colors.success : colors.primary};
229-
281+
230282
&::before {
231283
opacity: 1;
232284
}
233285
}
234-
235-
${props => props.$selected && `
286+
287+
&:focus-visible {
288+
outline: 2px solid ${colors.primary};
289+
outline-offset: 2px;
290+
}
291+
292+
${props => props.$selected && css`
293+
box-shadow: 0 0 0 1px ${colors.success}, 0 10px 28px rgba(92, 177, 118, 0.18);
294+
236295
&::after {
237296
content: '✓';
238297
position: absolute;
239-
top: 8px;
240-
right: 8px;
298+
top: 12px;
299+
right: 12px;
241300
width: 24px;
242301
height: 24px;
243302
background: ${colors.success};
244-
color: white;
303+
color: #fff;
245304
border-radius: 50%;
246305
display: flex;
247306
align-items: center;
248307
justify-content: center;
249-
font-size: 14px;
250-
font-weight: bold;
308+
font-size: 13px;
309+
font-weight: 700;
310+
box-shadow: 0 2px 6px rgba(92, 177, 118, 0.35);
251311
}
252312
`}
253313
`;
254314

255315
const ProviderIcon = styled.div`
256-
width: 56px;
257-
height: 56px;
316+
width: 48px;
317+
height: 48px;
258318
border-radius: ${props => props.$round ? '50%' : '12px'};
259-
background: ${props => props.$bgColor || colors.primaryLight};
319+
/* Dark-mode-friendly: mix the accent colour onto an opaque surface
320+
instead of using a semi-transparent wash. Semi-transparent colours
321+
disappeared against the plugin's dark modal background. */
322+
background: ${props => props.$bgColor || 'rgba(73, 69, 255, 0.2)'};
260323
display: flex;
261324
align-items: center;
262325
justify-content: center;
263-
font-size: ${props => props.$fontSize || '24px'};
264-
font-weight: bold;
326+
font-size: ${props => props.$fontSize || '22px'};
327+
font-weight: 700;
265328
color: ${props => props.$color || colors.primary};
266-
box-shadow: 0 4px 12px ${props => props.$shadowColor || 'rgba(73, 69, 255, 0.15)'};
329+
box-shadow: 0 4px 14px ${props => props.$shadowColor || 'rgba(73, 69, 255, 0.25)'};
330+
flex-shrink: 0;
331+
`;
332+
333+
const ProviderTextBlock = styled(Box)`
334+
display: flex;
335+
flex-direction: column;
336+
gap: 4px;
337+
margin-top: auto;
267338
`;
268339

269340
const ProviderName = styled(Typography)`
270341
font-weight: 600;
271-
font-size: 15px;
342+
font-size: 16px;
343+
line-height: 1.25;
272344
color: ${colors.text};
273345
margin: 0;
274346
`;
275347

276348
const ProviderTagline = styled(Typography)`
277-
font-size: 12px;
349+
font-size: 12.5px;
350+
line-height: 1.4;
278351
color: ${colors.textLight};
279352
margin: 0;
280353
`;
@@ -938,80 +1011,137 @@ const AddAccountModal = ({ isOpen, onClose, onAccountAdded, editAccount = null }
9381011
{/* Step 1: Choose Provider */}
9391012
{currentStep === 1 && (
9401013
<Box>
1014+
<ProviderCategoryHeader>
1015+
<ProviderCategoryLabel>OAuth Providers</ProviderCategoryLabel>
1016+
<ProviderCategoryHint>Recommended</ProviderCategoryHint>
1017+
</ProviderCategoryHeader>
9411018
<ProvidersGrid>
942-
<ProviderCard
1019+
<ProviderCard
9431020
$selected={provider === 'gmail-oauth'}
9441021
onClick={() => setProvider('gmail-oauth')}
1022+
role="button"
1023+
tabIndex={0}
1024+
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setProvider('gmail-oauth')}
9451025
>
946-
<ProviderIcon
947-
$round
948-
$bgColor="#4285F433"
1026+
<ProviderIcon
1027+
$round
1028+
$bgColor="rgba(66, 133, 244, 0.22)"
9491029
$color="#4285F4"
950-
$shadowColor="rgba(66, 133, 244, 0.2)"
1030+
$shadowColor="rgba(66, 133, 244, 0.3)"
9511031
>
9521032
G
9531033
</ProviderIcon>
954-
<ProviderName>Gmail OAuth</ProviderName>
1034+
<ProviderTextBlock>
1035+
<ProviderName>Gmail</ProviderName>
1036+
<ProviderTagline>Google Workspace & personal accounts · 1-click OAuth</ProviderTagline>
1037+
</ProviderTextBlock>
9551038
</ProviderCard>
9561039

957-
<ProviderCard
1040+
<ProviderCard
9581041
$selected={provider === 'microsoft-oauth'}
9591042
onClick={() => setProvider('microsoft-oauth')}
1043+
role="button"
1044+
tabIndex={0}
1045+
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setProvider('microsoft-oauth')}
9601046
>
961-
<ProviderIcon
962-
$round
963-
$bgColor="#00A4EF33"
1047+
<ProviderIcon
1048+
$round
1049+
$bgColor="rgba(0, 164, 239, 0.22)"
9641050
$color="#00A4EF"
965-
$shadowColor="rgba(0, 164, 239, 0.2)"
1051+
$shadowColor="rgba(0, 164, 239, 0.3)"
9661052
>
9671053
M
9681054
</ProviderIcon>
969-
<ProviderName>Microsoft OAuth</ProviderName>
1055+
<ProviderTextBlock>
1056+
<ProviderName>Microsoft 365</ProviderName>
1057+
<ProviderTagline>Outlook · Exchange · Azure AD · 1-click OAuth</ProviderTagline>
1058+
</ProviderTextBlock>
9701059
</ProviderCard>
9711060

972-
<ProviderCard
1061+
<ProviderCard
9731062
$selected={provider === 'yahoo-oauth'}
9741063
onClick={() => setProvider('yahoo-oauth')}
1064+
role="button"
1065+
tabIndex={0}
1066+
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setProvider('yahoo-oauth')}
9751067
>
976-
<ProviderIcon
977-
$round
978-
$bgColor="#6001D233"
979-
$color="#6001D2"
980-
$shadowColor="rgba(96, 1, 210, 0.2)"
1068+
<ProviderIcon
1069+
$round
1070+
$bgColor="rgba(96, 1, 210, 0.22)"
1071+
$color="#9C6BF0"
1072+
$shadowColor="rgba(96, 1, 210, 0.3)"
9811073
>
9821074
Y
9831075
</ProviderIcon>
984-
<ProviderName>Yahoo Mail OAuth</ProviderName>
1076+
<ProviderTextBlock>
1077+
<ProviderName>Yahoo Mail</ProviderName>
1078+
<ProviderTagline>Yahoo & AOL accounts · 1-click OAuth</ProviderTagline>
1079+
</ProviderTextBlock>
9851080
</ProviderCard>
1081+
</ProvidersGrid>
9861082

987-
<ProviderCard
1083+
<ProviderCategoryHeader>
1084+
<ProviderCategoryLabel>Custom & Transactional APIs</ProviderCategoryLabel>
1085+
</ProviderCategoryHeader>
1086+
<ProvidersGrid>
1087+
<ProviderCard
9881088
$selected={provider === 'smtp'}
9891089
onClick={() => setProvider('smtp')}
1090+
role="button"
1091+
tabIndex={0}
1092+
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setProvider('smtp')}
9901093
>
991-
<ProviderIcon>
992-
<Server style={{ width: 28, height: 28 }} />
1094+
<ProviderIcon
1095+
$bgColor="rgba(73, 69, 255, 0.22)"
1096+
$color="#7B78FF"
1097+
$shadowColor="rgba(73, 69, 255, 0.3)"
1098+
>
1099+
<Server style={{ width: 26, height: 26 }} />
9931100
</ProviderIcon>
994-
<ProviderName>SMTP</ProviderName>
1101+
<ProviderTextBlock>
1102+
<ProviderName>SMTP</ProviderName>
1103+
<ProviderTagline>Any SMTP server · full control over host, port & auth</ProviderTagline>
1104+
</ProviderTextBlock>
9951105
</ProviderCard>
9961106

997-
<ProviderCard
1107+
<ProviderCard
9981108
$selected={provider === 'sendgrid'}
9991109
onClick={() => setProvider('sendgrid')}
1110+
role="button"
1111+
tabIndex={0}
1112+
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setProvider('sendgrid')}
10001113
>
1001-
<ProviderIcon $bgColor="#1E90FF22" $color="#1E90FF" $shadowColor="rgba(30, 144, 255, 0.2)">
1002-
<Cloud style={{ width: 28, height: 28 }} />
1114+
<ProviderIcon
1115+
$bgColor="rgba(30, 144, 255, 0.22)"
1116+
$color="#4FA8FF"
1117+
$shadowColor="rgba(30, 144, 255, 0.3)"
1118+
>
1119+
<Cloud style={{ width: 26, height: 26 }} />
10031120
</ProviderIcon>
1004-
<ProviderName>SendGrid</ProviderName>
1121+
<ProviderTextBlock>
1122+
<ProviderName>SendGrid</ProviderName>
1123+
<ProviderTagline>Transactional API · high deliverability · analytics</ProviderTagline>
1124+
</ProviderTextBlock>
10051125
</ProviderCard>
10061126

1007-
<ProviderCard
1127+
<ProviderCard
10081128
$selected={provider === 'mailgun'}
10091129
onClick={() => setProvider('mailgun')}
1130+
role="button"
1131+
tabIndex={0}
1132+
onKeyDown={(e) => (e.key === 'Enter' || e.key === ' ') && setProvider('mailgun')}
10101133
>
1011-
<ProviderIcon $bgColor="#FF6B6B22" $color="#FF6B6B" $shadowColor="rgba(255, 107, 107, 0.2)">
1012-
<Mail style={{ width: 28, height: 28 }} />
1134+
<ProviderIcon
1135+
$bgColor="rgba(255, 107, 107, 0.22)"
1136+
$color="#FF8A8A"
1137+
$shadowColor="rgba(255, 107, 107, 0.3)"
1138+
>
1139+
<Mail style={{ width: 26, height: 26 }} />
10131140
</ProviderIcon>
1014-
<ProviderName>Mailgun</ProviderName>
1141+
<ProviderTextBlock>
1142+
<ProviderName>Mailgun</ProviderName>
1143+
<ProviderTagline>Developer-first API · EU/US regions · routes & webhooks</ProviderTagline>
1144+
</ProviderTextBlock>
10151145
</ProviderCard>
10161146
</ProvidersGrid>
10171147
</Box>

0 commit comments

Comments
 (0)