Skip to content

Commit f718b3f

Browse files
committed
Match next-cli auth pages styling
1 parent 7f44001 commit f718b3f

3 files changed

Lines changed: 146 additions & 3 deletions

File tree

internal/oauth/callback.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ type CallbackResult struct {
1313
Error string
1414
}
1515

16+
func writeBrowserResponse(w http.ResponseWriter, status int, state browserState) {
17+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
18+
w.WriteHeader(status)
19+
renderPage(w, state)
20+
}
21+
1622
func ListenForCallback(ctx context.Context) (port int, result <-chan CallbackResult, cleanup func(), err error) {
1723
listener, err := net.Listen("tcp", "127.0.0.1:0")
1824
if err != nil {
@@ -30,11 +36,17 @@ func ListenForCallback(ctx context.Context) (port int, result <-chan CallbackRes
3036
Error: q.Get("error"),
3137
}
3238

33-
w.Header().Set("Content-Type", "text/html; charset=utf-8")
3439
if res.Error != "" {
35-
_, _ = w.Write([]byte("<html><body><h2>Login was denied.</h2><p>You can close this tab.</p></body></html>"))
40+
writeBrowserResponse(w, http.StatusBadRequest, browserState{
41+
Title: "Authorization failed",
42+
Body: "Login was denied. You can close this tab.",
43+
})
3644
} else {
37-
_, _ = w.Write([]byte("<html><body><h2>Login successful!</h2><p>You can close this tab and return to the terminal.</p></body></html>"))
45+
writeBrowserResponse(w, http.StatusOK, browserState{
46+
Title: "Authorization successful",
47+
Body: "You can close this window and return to your terminal.",
48+
Success: true,
49+
})
3850
}
3951

4052
select {

internal/oauth/page.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package oauth
2+
3+
import (
4+
_ "embed"
5+
"io"
6+
"text/template"
7+
)
8+
9+
var (
10+
//go:embed page.html
11+
page string
12+
13+
browserResponseTmpl = template.Must(template.New("browser").Parse(page))
14+
)
15+
16+
type browserState struct {
17+
Title, Body string
18+
Success bool
19+
}
20+
21+
func renderPage(w io.Writer, state browserState) {
22+
_ = browserResponseTmpl.Execute(w, state)
23+
}

internal/oauth/page.html

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>{{.Title}} — CircleCI CLI</title>
6+
<style>
7+
*, *::before, *::after { box-sizing: border-box; }
8+
9+
:root {
10+
--body-bg: #1a2035;
11+
--card-bg: #222b3e;
12+
--card-border: rgba(255,255,255,0.08);
13+
--logo-fill: #ffffff;
14+
--heading-color: #ffffff;
15+
--text-color: #7b8db5;
16+
}
17+
18+
@media (prefers-color-scheme: light) {
19+
:root {
20+
--body-bg: #2b3757;
21+
--card-bg: #f0f3fa;
22+
--card-border: rgba(0,0,0,0.10);
23+
24+
--heading-color: #1a2035;
25+
--text-color: #5a6b8a;
26+
}
27+
}
28+
29+
body {
30+
margin: 0;
31+
min-height: 100vh;
32+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
33+
background-color: var(--body-bg);
34+
display: flex;
35+
flex-direction: column;
36+
align-items: center;
37+
justify-content: center;
38+
padding: 2rem;
39+
}
40+
41+
.logo {
42+
margin-bottom: 1.5rem;
43+
}
44+
45+
.logo svg {
46+
width: 4rem;
47+
height: 4rem;
48+
fill: var(--logo-fill);
49+
}
50+
51+
.card {
52+
background: var(--card-bg);
53+
border: 1px solid var(--card-border);
54+
border-radius: 0.875rem;
55+
padding: 2.5rem 2.5rem 2rem;
56+
max-width: 22rem;
57+
width: 100%;
58+
text-align: center;
59+
}
60+
61+
.checkmark {
62+
margin-bottom: 1rem;
63+
}
64+
65+
.checkmark svg {
66+
width: 2.25rem;
67+
height: 2.25rem;
68+
}
69+
70+
h1 {
71+
font-size: 1.125rem;
72+
font-weight: 700;
73+
color: var(--heading-color);
74+
margin: 0 0 0.5rem 0;
75+
letter-spacing: -0.01em;
76+
}
77+
78+
p {
79+
font-size: 0.875rem;
80+
color: var(--text-color);
81+
margin: 0;
82+
line-height: 1.5;
83+
}
84+
</style>
85+
</head>
86+
<body>
87+
<div class="logo">
88+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 103.8 105.2" aria-label="CircleCI">
89+
<path d="m 38.6,52.6 c 0,-6.9 5.6,-12.5 12.5,-12.5 6.9,0 12.5,5.6 12.5,12.5 0,6.9 -5.6,12.5 -12.5,12.5 C 44.2,65.2 38.6,59.5 38.6,52.6 Z M 51.1,0 C 26.5,0 5.9,16.8 0.1,39.6 0.1,39.8 0,39.9 0,40.1 c 0,1.4 1.1,2.5 2.5,2.5 l 21.2,0 c 1,0 1.9,-0.6 2.3,-1.5 l 0,0 C 30.4,31.6 39.9,25 51.1,25 66.3,25 78.7,37.4 78.7,52.6 78.7,67.8 66.3,80.2 51.1,80.2 40,80.2 30.4,73.6 26,64.1 l 0,0 c -0.4,-0.9 -1.3,-1.5 -2.3,-1.5 l -21.2,0 c -1.4,0 -2.5,1.1 -2.5,2.5 0,0.2 0,0.3 0.1,0.5 5.8,22.8 26.4,39.6 51,39.6 29.1,0 52.7,-23.6 52.7,-52.7 C 103.8,23.5 80.2,0 51.1,0 Z"/>
90+
</svg>
91+
</div>
92+
<div class="card">
93+
<div class="checkmark">
94+
{{if .Success}}
95+
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
96+
<path d="M7 18L15 26L29 10" stroke="#3d7a3d" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
97+
</svg>
98+
{{else}}
99+
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
100+
<path d="M10 10L26 26M26 10L10 26" stroke="#c0392b" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
101+
</svg>
102+
{{end}}
103+
</div>
104+
<h1>{{.Title}}</h1>
105+
<p>{{.Body}}</p>
106+
</div>
107+
</body>
108+
</html>

0 commit comments

Comments
 (0)