Skip to content

Commit 52c5a6b

Browse files
Add button states (#149)
This adds a demo of how button states could work. Before pressed: ![Screenshot 2025-01-09 at 14 46 18](https://github.com/user-attachments/assets/ddca4d23-1d85-4b3c-92d8-2ef3971dca74) When pressed: ![Screenshot 2025-01-09 at 14 46 32](https://github.com/user-attachments/assets/b86783bc-6fbf-4c66-8529-bb93f95478b8) If there’s an error (message would vary depending on error type): ![Screenshot 2025-01-09 at 14 46 55](https://github.com/user-attachments/assets/fb75944e-af41-45d5-855a-3a41ed7cdfb2)
1 parent 4361c99 commit 52c5a6b

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

app/assets/sass/components/_button.scss

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,58 @@
2121
}
2222
}
2323
}
24+
25+
26+
.app-button--loading {
27+
position: relative;
28+
-webkit-transition: width 5s ease;
29+
transition: width 5s ease
30+
}
31+
32+
.app-button--loading:before {
33+
position: relative;
34+
display: inline-block;
35+
margin-right: 10px;
36+
margin-bottom: -9px;
37+
height: 25px;
38+
width: 25px;
39+
background: transparent url('data:image/svg+xml,<svg height="200" viewBox="0 0 200 200" width="200" xmlns="http://www.w3.org/2000/svg">%0A <g fill="none" fill-rule="evenodd">%0A <path%0A d="m100 0v13.0000106c-.0149985-.0000068-.0299979-.0000106-.0449982-.0000106-48.0239214 0-86.9550018 38.9310804-86.9550018 86.9550018 0 48.0239212 38.9310804 86.9550022 86.9550018 86.9550022 48.0089212 0 86.9306812-38.906764 86.9549902-86.910004h13.090008c0 55.228475-44.771525 100-100 100s-100-44.771525-100-100 44.771525-100 100-100z"%0A fill="%23e5e5e5"/>%0A <path%0A d="m0 100h13.0000106zm100 86.909993v13.090007zm100-86.909993h-13.090007c.000007-.0149985.000011-.0299979.000011-.0449982 0-48.0089211-38.906764-86.9306807-86.910004-86.9549904v-13.0000114c55.228475 0 100 44.771525 100 100z"%0A fill="%23367c42"/>%0A </g>%0A</svg>') no-repeat;
40+
background-size: 25px 25px;
41+
-webkit-animation-name: spin;
42+
animation-name: spin;
43+
-webkit-animation-duration: .75s;
44+
animation-duration: .75s;
45+
-webkit-animation-iteration-count: infinite;
46+
animation-iteration-count: infinite;
47+
-webkit-transform-origin: 50% 50%;
48+
-ms-transform-origin: 50% 50%;
49+
transform-origin: 50% 50%;
50+
animation-timing-function: ease;
51+
52+
}
53+
54+
@keyframes spin {
55+
0% {
56+
-webkit-transform: rotate(0deg);
57+
transform:rotate(0)
58+
}
59+
60+
to {
61+
-webkit-transform: rotate(1turn);
62+
transform:rotate(1turn)
63+
}
64+
}
65+
66+
.app-button--loading[disabled],
67+
.app-button--loading[disabled]:hover {
68+
background-color: #44834d;
69+
opacity: 1;
70+
-webkit-box-shadow: 0 2px 0 #88988c;
71+
box-shadow:0 2px #88988c
72+
}
73+
74+
75+
.app-body--error {
76+
font-weight: bold;
77+
color: $nhsuk-error-color;
78+
}

app/views/button-states.html

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
{% extends 'layout.html' %}
2+
3+
{% block pageTitle %}
4+
Button states
5+
{% endblock %}
6+
7+
{% block content %}
8+
<div class="nhsuk-grid-row">
9+
<div class="nhsuk-grid-column-two-thirds">
10+
11+
<h1 class="nhsuk-heading-l">Button states</h1>
12+
13+
14+
<h2 class="nhsuk-heading-m">Not yet pressed</h2>
15+
16+
{{ button({
17+
text: "Confirm details and save"
18+
}) }}
19+
20+
<h2 class="nhsuk-heading-m">Pressed and being sent</h2>
21+
22+
{{ button({
23+
text: "Saving",
24+
classes: "app-button--loading",
25+
attributes: {
26+
disabled: true,
27+
"aria-live": "assertive",
28+
"aria-label": "Saving"
29+
}
30+
}) }}
31+
32+
<h2 class="nhsuk-heading-m">Example showing internet connection failing or timing out</h2>
33+
34+
<div class="app-button-group">
35+
{{ button({
36+
text: "Confirm details and save",
37+
attributes: {
38+
id: "button-timeout-example"
39+
}
40+
}) }}
41+
</div>
42+
43+
<h2 class="nhsuk-heading-m">Example showing server error (500)</h2>
44+
45+
<div class="app-button-group">
46+
{{ button({
47+
text: "Confirm details and save",
48+
attributes: {
49+
id: "button-server-error-example"
50+
}
51+
}) }}
52+
</div>
53+
54+
<h2 class="nhsuk-heading-m">Example showing server down (503)</h2>
55+
56+
<div class="app-button-group">
57+
{{ button({
58+
text: "Confirm details and save",
59+
attributes: {
60+
id: "button-server-down-example"
61+
}
62+
}) }}
63+
</div>
64+
65+
<script>
66+
67+
function setButtonAsSaving(button, errorMessage) {
68+
69+
button.disabled = true
70+
button.classList.add('app-button--loading')
71+
button.textContent = 'Saving'
72+
button.setAttribute('aria-live', 'assertive')
73+
button.setAttribute('aria-label', 'Saving')
74+
75+
let errorMessageElement = button.parentElement.querySelector('.app-body--error')
76+
77+
if (errorMessageElement) {
78+
errorMessageElement.remove()
79+
}
80+
81+
const randomTimeOut = ((1 + (5 * Math.random())) * 1000)
82+
83+
setTimeout(function() {
84+
this.textContent = 'Confirm details and save'
85+
this.disabled = false
86+
this.classList.remove('app-button--loading')
87+
this.removeAttribute('aria-live')
88+
this.removeAttribute('aria-label')
89+
this.insertAdjacentHTML("beforebegin", '<p class="app-body--error" aria-live="polite">' + errorMessage + '</p>')
90+
}.bind(event.target), randomTimeOut)
91+
92+
}
93+
94+
document.getElementById('button-server-error-example').addEventListener('click', function(event) {
95+
setButtonAsSaving(event.target, "Save failed due to server error. Try again.")
96+
})
97+
98+
document.getElementById('button-timeout-example').addEventListener('click', function(event) {
99+
setButtonAsSaving(event.target, "Save failed. Check your internet connection and try again.")
100+
})
101+
102+
document.getElementById('button-server-down-example').addEventListener('click', function(event) {
103+
setButtonAsSaving(event.target, "Save failed because of server error. Try again.")
104+
})
105+
106+
</script>
107+
108+
109+
</div>
110+
</div>
111+
112+
113+
{% endblock %}

0 commit comments

Comments
 (0)