From a0a8cceda41ea8832bcca92fc45e81edd9fbd3e8 Mon Sep 17 00:00:00 2001
From: windingwind <33902321+windingwind@users.noreply.github.com>
Date: Fri, 30 Aug 2024 19:20:52 +0800
Subject: [PATCH] Checkbox-and-radio-redesign-on-Windows (#4609)
---
.../preferences/preferences_advanced.xhtml | 14 ++--
.../zotero/win/dark/checkbox-active.svg | 4 +
.../zotero/win/dark/checkbox-disabled.svg | 3 +
.../zotero/win/dark/checkbox-hover.svg | 4 +
.../default/zotero/win/dark/checkbox-rest.svg | 4 +
.../win/dark/checkbox-selected-active.svg | 5 ++
.../win/dark/checkbox-selected-disabled.svg | 4 +
.../win/dark/checkbox-selected-hover.svg | 5 ++
.../win/dark/checkbox-selected-rest.svg | 4 +
.../default/zotero/win/dark/radio-active.svg | 5 ++
.../zotero/win/dark/radio-disabled.svg | 3 +
.../default/zotero/win/dark/radio-hover.svg | 4 +
.../default/zotero/win/dark/radio-rest.svg | 4 +
.../zotero/win/dark/radio-selected-active.svg | 11 +++
.../win/dark/radio-selected-disabled.svg | 4 +
.../zotero/win/dark/radio-selected-hover.svg | 11 +++
.../zotero/win/dark/radio-selected-rest.svg | 10 +++
.../zotero/win/light/checkbox-active.svg | 4 +
.../zotero/win/light/checkbox-disabled.svg | 3 +
.../zotero/win/light/checkbox-hover.svg | 4 +
.../zotero/win/light/checkbox-rest.svg | 4 +
.../win/light/checkbox-selected-active.svg | 5 ++
.../win/light/checkbox-selected-disabled.svg | 4 +
.../win/light/checkbox-selected-hover.svg | 5 ++
.../win/light/checkbox-selected-rest.svg | 4 +
.../default/zotero/win/light/radio-active.svg | 5 ++
.../zotero/win/light/radio-disabled.svg | 3 +
.../default/zotero/win/light/radio-hover.svg | 4 +
.../default/zotero/win/light/radio-rest.svg | 4 +
.../win/light/radio-selected-active.svg | 11 +++
.../win/light/radio-selected-disabled.svg | 4 +
.../zotero/win/light/radio-selected-hover.svg | 11 +++
.../zotero/win/light/radio-selected-rest.svg | 10 +++
scss/abstracts/_mixins.scss | 4 +-
scss/components/_banners.scss | 9 +++
scss/components/_search.scss | 2 +-
scss/preferences-win.scss | 2 +
scss/preferences.scss | 79 ++++++++++++++++---
scss/win/components/_checkbox.scss | 74 +++++++++++++++++
scss/win/components/_radio.scss | 70 ++++++++++++++++
scss/zotero-win.scss | 2 +
41 files changed, 407 insertions(+), 19 deletions(-)
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-active.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-hover.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-rest.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-selected-active.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-selected-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-selected-hover.svg
create mode 100644 chrome/skin/default/zotero/win/dark/checkbox-selected-rest.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-active.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-hover.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-rest.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-selected-active.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-selected-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-selected-hover.svg
create mode 100644 chrome/skin/default/zotero/win/dark/radio-selected-rest.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-active.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-hover.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-rest.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-selected-active.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-selected-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-selected-hover.svg
create mode 100644 chrome/skin/default/zotero/win/light/checkbox-selected-rest.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-active.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-hover.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-rest.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-selected-active.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-selected-disabled.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-selected-hover.svg
create mode 100644 chrome/skin/default/zotero/win/light/radio-selected-rest.svg
create mode 100644 scss/win/components/_checkbox.scss
create mode 100644 scss/win/components/_radio.scss
diff --git a/chrome/content/zotero/preferences/preferences_advanced.xhtml b/chrome/content/zotero/preferences/preferences_advanced.xhtml
index f217ecda6a..db70867659 100644
--- a/chrome/content/zotero/preferences/preferences_advanced.xhtml
+++ b/chrome/content/zotero/preferences/preferences_advanced.xhtml
@@ -28,11 +28,11 @@
-
+
-
-
+
@@ -249,19 +249,19 @@
-
+
&zotero.preferences.fulltext.textMaxLength;
(&zotero.preferences.default; 500000)
-
+
-
+
&zotero.preferences.fulltext.pdfMaxPages;
(&zotero.preferences.default; 100)
-
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-active.svg b/chrome/skin/default/zotero/win/dark/checkbox-active.svg
new file mode 100644
index 0000000000..0c130ef371
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-active.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-disabled.svg b/chrome/skin/default/zotero/win/dark/checkbox-disabled.svg
new file mode 100644
index 0000000000..162a94b3ad
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-hover.svg b/chrome/skin/default/zotero/win/dark/checkbox-hover.svg
new file mode 100644
index 0000000000..540ed25650
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-hover.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-rest.svg b/chrome/skin/default/zotero/win/dark/checkbox-rest.svg
new file mode 100644
index 0000000000..7669185c73
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-rest.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-selected-active.svg b/chrome/skin/default/zotero/win/dark/checkbox-selected-active.svg
new file mode 100644
index 0000000000..c6676f86c9
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-selected-active.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-selected-disabled.svg b/chrome/skin/default/zotero/win/dark/checkbox-selected-disabled.svg
new file mode 100644
index 0000000000..6a5900a0d8
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-selected-disabled.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-selected-hover.svg b/chrome/skin/default/zotero/win/dark/checkbox-selected-hover.svg
new file mode 100644
index 0000000000..149fd74aa3
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-selected-hover.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/checkbox-selected-rest.svg b/chrome/skin/default/zotero/win/dark/checkbox-selected-rest.svg
new file mode 100644
index 0000000000..6bbd945107
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/checkbox-selected-rest.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-active.svg b/chrome/skin/default/zotero/win/dark/radio-active.svg
new file mode 100644
index 0000000000..464ab1605e
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-active.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-disabled.svg b/chrome/skin/default/zotero/win/dark/radio-disabled.svg
new file mode 100644
index 0000000000..8ac9210b78
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-hover.svg b/chrome/skin/default/zotero/win/dark/radio-hover.svg
new file mode 100644
index 0000000000..920a7c72c6
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-hover.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-rest.svg b/chrome/skin/default/zotero/win/dark/radio-rest.svg
new file mode 100644
index 0000000000..7074a4d253
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-rest.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-selected-active.svg b/chrome/skin/default/zotero/win/dark/radio-selected-active.svg
new file mode 100644
index 0000000000..d5682bfb26
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-selected-active.svg
@@ -0,0 +1,11 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-selected-disabled.svg b/chrome/skin/default/zotero/win/dark/radio-selected-disabled.svg
new file mode 100644
index 0000000000..63d391a5c0
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-selected-disabled.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-selected-hover.svg b/chrome/skin/default/zotero/win/dark/radio-selected-hover.svg
new file mode 100644
index 0000000000..1d43a88592
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-selected-hover.svg
@@ -0,0 +1,11 @@
+
diff --git a/chrome/skin/default/zotero/win/dark/radio-selected-rest.svg b/chrome/skin/default/zotero/win/dark/radio-selected-rest.svg
new file mode 100644
index 0000000000..7255018090
--- /dev/null
+++ b/chrome/skin/default/zotero/win/dark/radio-selected-rest.svg
@@ -0,0 +1,10 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-active.svg b/chrome/skin/default/zotero/win/light/checkbox-active.svg
new file mode 100644
index 0000000000..bb5fc47807
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-active.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-disabled.svg b/chrome/skin/default/zotero/win/light/checkbox-disabled.svg
new file mode 100644
index 0000000000..934fc4bd81
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-hover.svg b/chrome/skin/default/zotero/win/light/checkbox-hover.svg
new file mode 100644
index 0000000000..c81458a8c4
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-hover.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-rest.svg b/chrome/skin/default/zotero/win/light/checkbox-rest.svg
new file mode 100644
index 0000000000..e45aa47630
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-rest.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-selected-active.svg b/chrome/skin/default/zotero/win/light/checkbox-selected-active.svg
new file mode 100644
index 0000000000..c6676f86c9
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-selected-active.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-selected-disabled.svg b/chrome/skin/default/zotero/win/light/checkbox-selected-disabled.svg
new file mode 100644
index 0000000000..d3ca7a3431
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-selected-disabled.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-selected-hover.svg b/chrome/skin/default/zotero/win/light/checkbox-selected-hover.svg
new file mode 100644
index 0000000000..149fd74aa3
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-selected-hover.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/win/light/checkbox-selected-rest.svg b/chrome/skin/default/zotero/win/light/checkbox-selected-rest.svg
new file mode 100644
index 0000000000..6bbd945107
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/checkbox-selected-rest.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-active.svg b/chrome/skin/default/zotero/win/light/radio-active.svg
new file mode 100644
index 0000000000..b8882e5a98
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-active.svg
@@ -0,0 +1,5 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-disabled.svg b/chrome/skin/default/zotero/win/light/radio-disabled.svg
new file mode 100644
index 0000000000..0b55b47ece
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-hover.svg b/chrome/skin/default/zotero/win/light/radio-hover.svg
new file mode 100644
index 0000000000..fce9306560
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-hover.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-rest.svg b/chrome/skin/default/zotero/win/light/radio-rest.svg
new file mode 100644
index 0000000000..d4f3565d2e
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-rest.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-selected-active.svg b/chrome/skin/default/zotero/win/light/radio-selected-active.svg
new file mode 100644
index 0000000000..68fb27839b
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-selected-active.svg
@@ -0,0 +1,11 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-selected-disabled.svg b/chrome/skin/default/zotero/win/light/radio-selected-disabled.svg
new file mode 100644
index 0000000000..96dec03e3d
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-selected-disabled.svg
@@ -0,0 +1,4 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-selected-hover.svg b/chrome/skin/default/zotero/win/light/radio-selected-hover.svg
new file mode 100644
index 0000000000..75b27121c9
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-selected-hover.svg
@@ -0,0 +1,11 @@
+
diff --git a/chrome/skin/default/zotero/win/light/radio-selected-rest.svg b/chrome/skin/default/zotero/win/light/radio-selected-rest.svg
new file mode 100644
index 0000000000..a464238851
--- /dev/null
+++ b/chrome/skin/default/zotero/win/light/radio-selected-rest.svg
@@ -0,0 +1,10 @@
+
diff --git a/scss/abstracts/_mixins.scss b/scss/abstracts/_mixins.scss
index 56dc194758..f7f2d62e11 100644
--- a/scss/abstracts/_mixins.scss
+++ b/scss/abstracts/_mixins.scss
@@ -275,8 +275,8 @@
components (e.g. toolbarbutton) and sometimes are too wide (e.g. around textfield on macOS).
Box-shadow is used to be able to set the radius.
*/
-@mixin focus-ring($thin: false, $selector: focus-visible) {
- &:#{$selector} {
+@mixin focus-ring($thin: false, $selector: ":focus-visible") {
+ {$selector} {
@media (-moz-platform: windows) {
outline: var(--color-focus-outer-border) solid var(--width-focus-outer-border);
outline-offset: var(--width-focus-border);
diff --git a/scss/components/_banners.scss b/scss/components/_banners.scss
index c64336c29d..361218c6bd 100644
--- a/scss/components/_banners.scss
+++ b/scss/components/_banners.scss
@@ -39,6 +39,15 @@
flex-wrap: wrap;
gap: 8px;
flex: 1 auto;
+
+ #post-upgrade-density {
+ @media (-moz-platform: windows) {
+ radio {
+ height: 24px;
+ margin: 0;
+ }
+ }
+ }
}
#post-upgrade-new-features-link {
diff --git a/scss/components/_search.scss b/scss/components/_search.scss
index b8655ccffd..88c098cc18 100644
--- a/scss/components/_search.scss
+++ b/scss/components/_search.scss
@@ -55,7 +55,7 @@ search-textbox {
}
@media not (-moz-platform: windows) {
- @include focus-ring(false, focus);
+ @include focus-ring(false, ":focus");
}
}
diff --git a/scss/preferences-win.scss b/scss/preferences-win.scss
index e17feb8783..52d83e0985 100644
--- a/scss/preferences-win.scss
+++ b/scss/preferences-win.scss
@@ -18,7 +18,9 @@
// Components
@import "win/components/button";
+@import "win/components/checkbox";
@import "win/components/input";
@import "win/components/menulist";
@import "win/components/menupopup";
+@import "win/components/radio";
@import "win/components/tabbox";
diff --git a/scss/preferences.scss b/scss/preferences.scss
index 50c78e5add..4abf02c457 100644
--- a/scss/preferences.scss
+++ b/scss/preferences.scss
@@ -300,19 +300,80 @@ label[is=zotero-text-link] {
}
}
-.pref-row {
- display: flex;
- align-items: center;
-}
-
button {
flex-shrink: 0;
}
-radiogroup {
- margin-bottom: 4px;
+@media (-moz-platform: windows) {
+ // Adjust padding and margin of form elements on Windows
+ // Assumptions:
+ // 1. All radio are in radiogroup
+ // 2. All radiogroup/checkbox are in hbox/vbox/groupbox
+ // 3. Vertical radiogroup are only in vertical layout
+
+ // First and last checkbox in a horizontal layout should not have inline padding and margin
+ hbox > checkbox[native]{
+ &:first-child {
+ padding-inline-start: 0;
+ margin-inline-start: 0;
+ }
+ &:last-child {
+ padding-inline-end: 0;
+ margin-inline-end: 0;
+ }
+ }
+
+ vbox, groupbox {
+ // Checkbox in a vertical layout should not have inline padding and margin
+ & > checkbox[native] {
+ padding-inline: 0;
+ margin-inline: 0;
+ }
+
+ // Button-like elements in a vertical layout should not have inline margin
+ & > button,
+ & > menulist {
+ margin-inline: 0;
+ }
+
+ // Radiogroup in a vertical layout and radio in a horizontal layout
+ // First and last radio should not have inline padding and margin
+ & > radiogroup[orient=horizontal] radio {
+ &:first-child {
+ padding-inline-start: 0;
+ margin-inline-start: 0;
+ }
+ &:last-child {
+ padding-inline-end: 0;
+ margin-inline-end: 0;
+ }
+ }
+
+ // In horizontal layout inside vertical layout
+ // First and last button-like element should not have inline margin
+ & > hbox > :is(button, menulist) {
+ &:first-child {
+ margin-inline-start: 0;
+ }
+ &:last-child {
+ margin-inline-end: 0;
+ }
+ }
+ }
+
+ // Radio in a vertical layout should not have inline padding and margin
+ radiogroup:not([orient=horizontal]) radio {
+ padding-inline: 0;
+ margin-inline: 0;
+ }
}
-radio, checkbox {
- margin-block: 4px;
+@media not (-moz-platform: windows) {
+ radiogroup {
+ margin-bottom: 4px;
+ }
+
+ radio, checkbox {
+ margin-block: 4px;
+ }
}
diff --git a/scss/win/components/_checkbox.scss b/scss/win/components/_checkbox.scss
new file mode 100644
index 0000000000..ce0eba6aee
--- /dev/null
+++ b/scss/win/components/_checkbox.scss
@@ -0,0 +1,74 @@
+@mixin -make-checkbox-icon($state) {
+ & :is(.checkbox-check) {
+ @include focus-states using ($color) {
+ background-image: url('chrome://zotero/skin/win/#{$color}/checkbox-#{$state}.svg');
+
+ &[checked=true] {
+ background-image: url('chrome://zotero/skin/win/#{$color}/checkbox-selected-#{$state}.svg');
+ }
+ }
+ }
+}
+
+:is(checkbox[native]) {
+ appearance: none;
+ height: 28px;
+ gap: 8px;
+ padding-inline: 4px 8px;
+ border-radius: 4px;
+
+ --checkbox-border-color: transparent;
+ --checkbox-unchecked-bgcolor: transparent;
+ --checkbox-unchecked-hover-bgcolor: transparent;
+ --checkbox-unchecked-active-bgcolor: transparent;
+ --checkbox-checked-border-color: transparent;
+ --checkbox-checked-bgcolor: transparent;
+ --checkbox-checked-color: transparent;
+ --checkbox-checked-hover-bgcolor: transparent;
+ --checkbox-checked-active-bgcolor: transparent;
+
+ .checkbox-check {
+ appearance: none;
+ background-position: center;
+ background-repeat: no-repeat;
+ width: 16px;
+ height: 16px;
+ border-radius: 4px;
+ margin-inline-end: 0px;
+ }
+
+ @include -make-checkbox-icon("rest");
+
+ &:not([disabled]) {
+ &:hover {
+ @include -make-checkbox-icon("hover");
+ }
+
+ &:hover:active {
+ @include -make-checkbox-icon("active");
+ }
+ }
+
+ &:disabled,
+ &[disabled] {
+ @include -make-checkbox-icon("disabled");
+ }
+}
+
+checkbox[native]:focus-visible {
+ & > .checkbox-label-box {
+ outline: none;
+ }
+
+ &:not([disabled]) {
+ &[label] {
+ @include focus-ring(false, "");
+ }
+
+ &:not([label]) {
+ & :is(.checkbox-check) {
+ @include focus-ring(false, "");
+ }
+ }
+ }
+}
diff --git a/scss/win/components/_radio.scss b/scss/win/components/_radio.scss
new file mode 100644
index 0000000000..6033830669
--- /dev/null
+++ b/scss/win/components/_radio.scss
@@ -0,0 +1,70 @@
+@mixin -make-radio-icon($state) {
+ & :is(.radio-check) {
+ @include focus-states using ($color) {
+ background-image: url('chrome://zotero/skin/win/#{$color}/radio-#{$state}.svg');
+
+ &[selected=true] {
+ background-image: url('chrome://zotero/skin/win/#{$color}/radio-selected-#{$state}.svg');
+ }
+ }
+ }
+}
+
+radio {
+ appearance: none;
+ height: 28px;
+ gap: 8px;
+ padding-inline: 4px 8px;
+ border-radius: 4px;
+
+ .radio-check {
+ appearance: none;
+ background-position: center;
+ background-repeat: no-repeat;
+ width: 16px;
+ height: 16px;
+ border-radius: 8px;
+ }
+
+ .radio-label-box {
+ padding-inline-start: 0;
+ margin-inline-start: 0;
+ }
+
+ @include -make-radio-icon("rest");
+
+ &:not([disabled]) {
+ &:hover {
+ @include -make-radio-icon("hover");
+ }
+
+ &:hover:active {
+ @include -make-radio-icon("active");
+ }
+
+ @include focus-ring;
+ }
+
+ &:disabled,
+ &[disabled] {
+ @include -make-radio-icon("disabled");
+ }
+}
+
+radiogroup:focus-visible {
+ & > radio[focused=true] {
+ & > .radio-label-box {
+ outline: none;
+ }
+
+ &[label] {
+ @include focus-ring(false, "");
+ }
+
+ &:not([label]) {
+ & :is(.radio-check) {
+ @include focus-ring(false, "");
+ }
+ }
+ }
+}
diff --git a/scss/zotero-win.scss b/scss/zotero-win.scss
index 2d52528d30..234953f486 100644
--- a/scss/zotero-win.scss
+++ b/scss/zotero-win.scss
@@ -17,7 +17,9 @@
// Components
@import "win/components/button";
+@import "win/components/checkbox";
@import "win/components/input";
@import "win/components/menulist";
@import "win/components/menupopup";
+@import "win/components/radio";
@import "win/components/tabbox";