Skip to content

Commit 8186f13

Browse files
Merge pull request #98 from WebDevSimplified/mobile-freebie-btn
Add Mobile View Freebies
2 parents 77c5b77 + 10a655e commit 8186f13

3 files changed

Lines changed: 378 additions & 155 deletions

File tree

Lines changed: 173 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
import type { Freebie } from "../data/freebies"
3-
import Tag from "./Tag"
3+
import FreebieSidebarContent from "./FreebieSidebarContent.astro"
44
55
export interface Props {
66
freebie: Freebie
@@ -10,56 +10,57 @@ const { freebie } = Astro.props
1010
---
1111

1212
<script async src="https://f.convertkit.com/ckjs/ck.6.js"></script>
13-
<aside class="freebie-sidebar" data-freebie-sidebar>
14-
<img src={freebie.image} alt={freebie.title} class="freebie-image" />
15-
<span class="freebie-badge">FREE</span>
16-
<h3 class="freebie-title">{freebie.title}</h3>
17-
<p class="freebie-description">{freebie.description}</p>
18-
<form
19-
action={`https://app.kit.com/forms/${freebie.kitFormId}/subscriptions`}
20-
method="post"
21-
class="freebie-form"
22-
data-sv-form={freebie.kitFormId}
23-
data-uid={freebie.kitFormUrlId}
24-
data-freebie-form
25-
data-element="content"
26-
>
27-
<div data-element="errors" class="hidden"></div>
28-
<div data-element="fields" style="display: contents;" data-fields-wrapper>
29-
<input
30-
type="text"
31-
name="fields[first_name]"
32-
placeholder="First Name"
33-
class="freebie-input"
34-
aria-label="First Name"
35-
/>
36-
<input
37-
type="email"
38-
name="email_address"
39-
placeholder="Email Address"
40-
required
41-
class="freebie-input"
42-
aria-label="Email Address"
43-
/>
44-
<button data-element="submit" type="submit" class="freebie-submit"
45-
>Download Now</button
46-
>
47-
</div>
48-
</form>
49-
<p class="freebie-privacy" data-freebie-privacy>
50-
No spam. Unsubscribe anytime.
51-
</p>
52-
<p class="freebie-success hidden" data-freebie-success>
53-
Thanks for signing up for my {freebie.title}! Check your email to download
54-
it!
55-
</p>
13+
14+
<!-- Desktop sidebar -->
15+
<aside class="freebie-sidebar">
16+
<FreebieSidebarContent freebie={freebie} />
5617
</aside>
5718

58-
<style>
59-
.hidden {
60-
display: none;
61-
}
19+
<!-- Mobile button + dialog drawer -->
20+
<button class="freebie-mobile-btn freebie-btn" data-freebie-open>
21+
<svg
22+
width="800px"
23+
height="800px"
24+
viewBox="0 0 24 24"
25+
fill="none"
26+
xmlns="http://www.w3.org/2000/svg"
27+
>
28+
<path
29+
d="M3 15C3 17.8284 3 19.2426 3.87868 20.1213C4.75736 21 6.17157 21 9 21H15C17.8284 21 19.2426 21 20.1213 20.1213C21 19.2426 21 17.8284 21 15"
30+
stroke="currentColor"
31+
stroke-width="1.5"
32+
stroke-linecap="round"
33+
stroke-linejoin="round"></path>
34+
<path
35+
d="M12 3V16M12 16L16 11.625M12 16L8 11.625"
36+
stroke="currentColor"
37+
stroke-width="1.5"
38+
stroke-linecap="round"
39+
stroke-linejoin="round"></path>
40+
</svg>Free {freebie.shortTitle}</button
41+
>
42+
43+
<dialog class="freebie-dialog" data-freebie-dialog>
44+
<button class="freebie-close-btn" data-freebie-close aria-label="Close">
45+
<svg
46+
width="24"
47+
height="24"
48+
viewBox="0 0 24 24"
49+
fill="none"
50+
xmlns="http://www.w3.org/2000/svg"
51+
>
52+
<path
53+
d="M18 6L6 18M6 6L18 18"
54+
stroke="currentColor"
55+
stroke-width="2"
56+
stroke-linecap="round"
57+
stroke-linejoin="round"></path>
58+
</svg>
59+
</button>
60+
<FreebieSidebarContent freebie={freebie} />
61+
</dialog>
6262

63+
<style>
6364
.freebie-sidebar {
6465
position: sticky;
6566
top: 1rem;
@@ -72,121 +73,135 @@ const { freebie } = Astro.props
7273
border: 1px solid var(--theme-tangent-border);
7374
border-radius: 0.75rem;
7475
padding: 1.25rem;
75-
display: flex;
76-
flex-direction: column;
77-
gap: 0.5rem;
78-
}
79-
80-
.freebie-image {
81-
width: 100%;
82-
object-fit: cover;
83-
object-position: top;
84-
filter: drop-shadow(0 0 10px #000b);
85-
min-height: 0;
86-
margin-bottom: 0.5rem;
87-
}
88-
89-
.freebie-badge {
90-
width: fit-content;
91-
background-color: var(--theme-freebie-button);
92-
color: var(--theme-freebie-button-text);
93-
font-size: 0.75rem;
94-
font-weight: 600;
95-
padding: 0.25rem 0.5rem;
96-
border-radius: 0.25rem;
97-
text-transform: uppercase;
98-
}
9976

100-
.freebie-title {
101-
margin: 0;
102-
font-size: 1.25rem;
103-
font-weight: 600;
104-
color: var(--theme-text);
105-
line-height: 1.3;
106-
margin-bottom: -0.25rem;
107-
}
108-
109-
.freebie-description {
110-
margin: 0;
111-
font-size: 0.9rem;
112-
color: var(--theme-text-light);
113-
line-height: 1.5;
114-
white-space: pre-wrap;
115-
}
116-
117-
.freebie-form {
11877
display: flex;
119-
flex-direction: column;
120-
gap: 0.5rem;
12178
}
12279

123-
.freebie-input {
124-
padding: 0.75rem 1rem;
125-
font-size: 0.95rem;
126-
border: 1px solid var(--theme-tangent-border);
127-
border-radius: 0.5rem;
128-
background-color: var(--theme-bg);
129-
color: var(--theme-text);
130-
font-family: inherit;
131-
transition:
132-
border-color 0.2s ease,
133-
box-shadow 0.2s ease;
134-
}
135-
136-
.freebie-input:focus-visible {
137-
outline: none;
138-
border-color: var(--theme-accent);
139-
box-shadow: 0 0 0 3px var(--theme-bg-accent);
140-
}
141-
142-
.freebie-input::placeholder {
143-
color: var(--theme-text-lighter);
144-
}
145-
146-
.freebie-submit {
80+
.freebie-btn {
14781
padding: 0.75rem 1rem;
14882
font-size: 1rem;
149-
font-weight: 600;
15083
font-family: inherit;
15184
color: var(--theme-freebie-button-text);
15285
background-color: var(--theme-freebie-button);
15386
border: none;
15487
border-radius: 0.5rem;
15588
cursor: pointer;
156-
transition:
157-
background-color 0.2s ease,
158-
transform 0.1s ease;
89+
transition: background-color 0.2s ease;
15990
}
16091

161-
.freebie-submit:hover {
92+
.freebie-btn:hover {
16293
background-color: var(--theme-freebie-button-hover);
16394
}
16495

165-
.freebie-submit:active {
166-
transform: scale(0.98);
96+
.freebie-mobile-btn {
97+
display: none;
98+
position: fixed;
99+
bottom: 1rem;
100+
right: 1rem;
101+
align-items: center;
102+
gap: 0.5rem;
103+
z-index: 999;
167104
}
168105

169-
.freebie-privacy {
170-
margin: 0;
171-
font-size: 0.75rem;
172-
color: var(--theme-text-light);
173-
text-align: center;
106+
.freebie-mobile-btn svg {
107+
width: 1.25rem;
108+
height: 1.25rem;
109+
vertical-align: middle;
174110
}
175111

176-
.freebie-success {
177-
margin: 0;
178-
font-size: 0.9rem;
179-
background: var(--theme-green);
112+
/* Dialog drawer styles */
113+
.freebie-dialog {
114+
display: none;
115+
}
116+
117+
.freebie-dialog::backdrop {
118+
background: rgba(0, 0, 0, 0.5);
119+
opacity: 0;
120+
}
121+
122+
.freebie-dialog[open]::backdrop {
123+
opacity: 1;
124+
}
125+
126+
.freebie-close-btn {
127+
position: absolute;
128+
top: 1rem;
129+
right: 1rem;
130+
align-self: flex-end;
131+
background: none;
132+
border: none;
180133
color: var(--theme-text);
181-
border-radius: 0.5rem;
182-
padding: 0.75em 1em;
183-
text-align: center;
134+
cursor: pointer;
135+
padding: 0.25rem;
136+
border-radius: 0.25rem;
137+
display: flex;
138+
align-items: center;
139+
justify-content: center;
140+
transition: background-color 0.2s ease;
141+
}
142+
143+
.freebie-close-btn:hover {
144+
background-color: var(--theme-tangent-border);
184145
}
185146

186147
@media (max-width: 1100px) {
187148
.freebie-sidebar {
188149
display: none;
189150
}
151+
152+
.freebie-mobile-btn {
153+
display: flex;
154+
}
155+
156+
.freebie-dialog {
157+
position: fixed;
158+
top: 0;
159+
right: 0;
160+
bottom: 0;
161+
left: auto;
162+
margin: 0;
163+
margin-left: auto;
164+
width: 100%;
165+
max-width: 360px;
166+
height: 100dvh;
167+
max-height: 100dvh;
168+
169+
background-color: var(--theme-tangent-bg);
170+
border: none;
171+
border-left: 1px solid var(--theme-tangent-border);
172+
padding: 1.25rem;
173+
align-items: center;
174+
175+
translate: 100%;
176+
transition:
177+
translate 0.3s ease-in-out,
178+
overlay 0.3s allow-discrete,
179+
display 0.3s allow-discrete;
180+
}
181+
182+
.freebie-dialog[open] {
183+
display: flex;
184+
translate: 0;
185+
}
186+
187+
@starting-style {
188+
.freebie-dialog[open] {
189+
translate: 100%;
190+
}
191+
}
192+
193+
.freebie-dialog::backdrop {
194+
transition:
195+
opacity 0.3s ease-in-out,
196+
overlay 0.3s allow-discrete,
197+
display 0.3s allow-discrete;
198+
}
199+
200+
@starting-style {
201+
.freebie-dialog[open]::backdrop {
202+
opacity: 0;
203+
}
204+
}
190205
}
191206

192207
:root {
@@ -203,27 +218,30 @@ const { freebie } = Astro.props
203218
</style>
204219

205220
<script>
206-
const forms = document.querySelectorAll("[data-freebie-form]")
207-
208-
const observer = new MutationObserver(mutations => {
209-
mutations.forEach(mutation => {
210-
if (mutation.type !== "childList") return
211-
212-
mutation.addedNodes.forEach(node => {
213-
if (node instanceof HTMLElement && node.matches("[data-element]")) {
214-
const sidebar = node.closest("[data-freebie-sidebar]")
215-
if (sidebar == null) return
216-
node.remove()
217-
sidebar.querySelector("[data-freebie-privacy]")?.remove()
218-
sidebar
219-
.querySelector("[data-freebie-success]")
220-
?.classList.remove("hidden")
221-
}
222-
})
223-
})
221+
const openBtn = document.querySelector("[data-freebie-open]")
222+
const dialog = document.querySelector(
223+
"[data-freebie-dialog]",
224+
) as HTMLDialogElement
225+
const closeBtn = document.querySelector("[data-freebie-close]")
226+
227+
openBtn?.addEventListener("click", () => {
228+
dialog?.showModal()
229+
})
230+
231+
closeBtn?.addEventListener("click", () => {
232+
dialog?.close()
224233
})
225234

226-
forms.forEach(form => {
227-
observer.observe(form, { childList: true })
235+
// Close when clicking the backdrop
236+
dialog?.addEventListener("click", e => {
237+
const rect = dialog.getBoundingClientRect()
238+
if (
239+
e.clientX < rect.left ||
240+
e.clientX > rect.right ||
241+
e.clientY < rect.top ||
242+
e.clientY > rect.bottom
243+
) {
244+
dialog.close()
245+
}
228246
})
229247
</script>

0 commit comments

Comments
 (0)