Files
grecia/app/assets/stylesheets/participation.scss
Javi Martín a7e1b42b6c Use checkboxes and radio buttons on poll forms
Our original interface to vote in a poll had a few issues:

* Since there was no button to send the form, it wasn't clear that
  selecting an option would automatically store it in the database.
* The interface was almost identical for single-choice questions and
  multiple-choice questions, which made it hard to know which type of
  question we were answering.
* Adding other type of questions, like open answers, was hard since we
  would have to add a different submit button for each answer.

So we're now using radio buttons for single-choice questions and
checkboxes for multiple-choice questions, which are the native controls
designed for these purposes, and a button to send the whole form.

Since we don't have a database table for poll ballots like we have for
budget ballots, we're adding a new `Poll::WebVote` model to manage poll
ballots. We're using WebVote instead of Ballot or Vote because they
could be mistaken with other vote classes.

Note that browsers don't allow removing answers with radio buttons, so
once somebody has voted in a single-choice question, they can't remove
the vote unless they manually edit their HTML. This is the same behavior
we had before commit 7df0e9a96.

As mentioned in c2010f975, we're now adding the `ChangeByZero` rubocop
rule, since we've removed the test that used `and change`.
2025-08-14 13:06:37 +02:00

1445 lines
23 KiB
SCSS

@use "sass:color";
// Table of Contents
//
// 01. Votes and supports
// 02. New participation
// 03. Show participation
// 04. List participation
// 05. Featured
// 06. Budgets
// 07. Proposals successful
// 08. Polls
// 09. Polls results and stats
//
// 01. Votes and supports
// ----------------------
@mixin supports {
padding: $line-height 0;
position: relative;
.progress {
background: color.adjust($proposals, $lightness: 35%);
border: 1px solid color.adjust($proposals, $lightness: 35%);
height: rem-calc(14);
position: relative;
.meter {
background: $proposals;
border-radius: 0;
border-bottom-right-radius: rem-calc(3);
border-top-right-radius: rem-calc(3);
display: block;
height: calc($line-height / 2);
}
}
.percentage {
font-size: $tiny-font-size;
position: absolute;
right: 6px;
top: -2px;
vertical-align: top;
}
abbr {
color: $text-medium;
&[title] {
border-bottom: 0;
}
}
.button-support {
background: $proposals;
color: inherit;
display: inline-block;
font-size: $small-font-size;
margin-top: rem-calc(12);
&:hover,
&:active {
background: color.adjust($proposals, $lightness: 25%);
cursor: pointer;
}
}
.total-supports {
display: block;
font-weight: bold;
text-align: center;
span {
display: block;
font-size: $small-font-size;
font-weight: normal;
}
}
.divider {
margin: 0 rem-calc(6);
}
.supported {
color: inherit;
margin-top: rem-calc(12);
}
}
.participation-not-allowed {
background: $warning-bg;
color: $color-warning;
display: none;
line-height: $line-height;
padding: calc($line-height / 2);
text-align: center;
width: 100%;
&,
p {
font-size: $small-font-size;
}
p {
color: inherit !important;
margin: 0 rem-calc(12);
text-align: left;
}
a {
color: inherit !important;
font-weight: bold;
text-decoration: underline;
}
}
// 02. New participation
// ---------------------
.recommendations {
list-style-type: none;
margin-left: 0;
margin-top: $line-height;
li {
@include has-fa-icon(check, solid);
margin: calc($line-height / 2) 0;
&::before {
margin-right: $font-icon-margin * 1.5;
}
}
}
.budget-investment-new,
.proposal-new,
.proposal-edit,
.polls-form,
.poll-question-form,
.legislation-process-new,
.legislation-process-edit,
.milestone-new,
.milestone-edit,
.image-form,
.dashboard-action-form {
@include direct-uploads;
}
// 03. Show participation
// ----------------------
.debate-show,
.proposal-show,
.budget-investment-show,
.polls-show,
.draft-panels,
.debate-questions,
.communities-show,
.topic-show,
.milestone-content {
h1,
p {
word-wrap: break-word;
}
.callout {
&.proposal-retired {
font-size: $base-font-size;
}
}
.social-share-full .social-share-button {
display: inline;
}
.debate-info,
.proposal-info,
.investment-project-info,
.budget-investment-show,
.topic-info {
clear: both;
color: $text-medium;
font-size: $small-font-size;
margin-bottom: calc($line-height / 2);
position: relative;
span:not(.label) {
line-height: rem-calc(32); // Same as avatar height
}
a {
color: $text-medium;
}
p {
font-size: $small-font-size;
line-height: $line-height;
margin: 0;
}
}
.debate-description,
.proposal-description {
font-size: rem-calc(15);
line-height: rem-calc(30);
}
ul,
ol {
li {
font-size: rem-calc(15);
margin-bottom: rem-calc(15);
}
.order-links > li {
font-size: inherit;
margin-bottom: 0;
}
}
.geozone {
li {
display: inline-block;
margin-bottom: 0;
}
}
.author-photo {
line-height: rem-calc(32);
margin-right: rem-calc(6);
vertical-align: top;
width: 32px;
}
.author {
font-weight: bold;
a {
@include link;
}
}
aside {
h2 {
@include brand-border(top, 2px);
display: inline-block;
font-size: rem-calc(16);
margin: -1px 0 rem-calc(12);
padding-top: rem-calc(6);
text-transform: uppercase;
}
}
blockquote {
clear: both;
color: #4c4c4c;
font-size: rem-calc(15);
line-height: rem-calc(30);
margin-top: rem-calc(12);
padding-top: 0;
}
.video-link {
background: $highlight-soft;
border: 1px solid $highlight;
display: block;
margin: calc($line-height / 2) 0;
padding: calc($line-height / 2);
position: relative;
a {
word-wrap: break-word;
}
.icon-video {
color: #cc181e;
display: inline-block;
font-size: rem-calc(24);
line-height: $line-height;
vertical-align: middle;
}
p {
margin-bottom: 0;
}
}
.supports {
@include supports;
}
.tags {
@extend %tags;
}
.tags,
.sdg-target-tag-list {
a {
margin-right: rem-calc(6);
}
}
}
.debate-questions .debate-questions .participation-not-allowed {
display: block;
}
.bullet {
color: $body-font-color;
}
.budget-investment-show p {
word-break: break-word;
}
.proposal-show,
.budget-investment-show {
.supports {
padding: calc($line-height / 2) 0 0;
}
.share-supported {
display: none;
}
}
.show-actions-menu {
[class^="icon-"] {
display: inline-block;
vertical-align: middle;
}
}
.additional-content {
.tabs {
background: none;
border: 0;
margin-bottom: 0;
a {
background: none;
}
}
.filter-subnav {
@include full-width-background;
background: $highlight;
margin-bottom: $line-height;
padding-top: calc($line-height / 4);
}
}
.fixed-mobile-content {
@include breakpoint(medium down) {
background: $body-background;
margin-bottom: rem-calc(-1) !important;
padding-top: calc($line-height / 2);
}
}
// 04. List participation
// ----------------------
.debates-list,
.proposals-list,
.budget-investments-list {
@include breakpoint(medium) {
margin-bottom: rem-calc(48);
}
}
.proposals-list,
.budget-investments-list {
@include breakpoint(medium) {
min-height: $line-height * 15;
}
}
.debates-list,
.proposals-list,
.budget-investments-list,
.legislation-proposals {
.panel {
column-gap: calc(rem-calc(map-get($grid-column-gutter, medium)) * 3 / 4);
display: flex;
flex-wrap: wrap;
> * {
flex-grow: 1;
}
&.with-image {
padding-bottom: 0;
padding-top: 0;
.panel-image {
margin-#{$global-left}: rem-calc(-12);
text-align: center;
~ * {
padding-top: calc($line-height / 2);
}
}
img {
height: 100%;
}
}
.debate-content,
.budget-investment-content,
.proposal-content {
flex-basis: calc((35rem - 100%) * 999);
flex-grow: 1000;
max-width: 50rem;
+ * {
flex-basis: 22.5%;
flex-shrink: 0;
text-align: center;
}
}
}
}
%panel {
border: 1px solid;
border-color: #e5e6e9 #dfe0e4 #d0d1d5;
border-radius: 0;
box-shadow: 0 1px 3px 0 $border;
margin-bottom: rem-calc(12);
min-height: rem-calc(192);
padding: rem-calc(12) rem-calc(12) 0;
@include breakpoint(medium) {
margin-bottom: rem-calc(-1);
padding-bottom: rem-calc(12);
}
@include breakpoint(medium) {
.divider {
display: inline-block;
}
}
h3 {
font-weight: bold;
margin-top: calc($line-height / 2);
a {
color: inherit;
display: inline-block;
}
}
&.past-budgets {
display: flex;
flex-wrap: wrap;
min-height: 0;
> :not(:first-child) {
margin-left: auto;
}
.button {
margin-left: $line-height;
}
}
}
.debate,
.proposal,
.budget-investment,
.budget-investment-show,
.legislation,
.communities-show {
margin: calc($line-height / 4) 0;
.panel {
@extend %panel;
}
.debate-content,
.proposal-content,
.budget-investment-content {
margin: 0;
min-height: rem-calc(180);
position: relative;
}
.tags {
@extend %tags;
}
.tags,
.sdg-target-tag-list {
a {
font-size: $tiny-font-size;
}
}
.debate-info,
.proposal-info,
.investment-project-info,
.budget-investment-info,
.topic-info {
color: $text-medium;
font-size: $small-font-size;
margin: rem-calc(6) 0 0;
.icon-comments {
font-size: rem-calc(16);
vertical-align: top;
}
a {
color: $text-medium;
}
}
.debate-description,
.proposal-description,
.investment-project-description {
font-size: rem-calc(13);
height: rem-calc(72);
line-height: $line-height;
margin-bottom: rem-calc(12);
margin-top: 0;
overflow: hidden;
position: relative;
}
.truncate {
background: image-url("truncate.png");
background-repeat: repeat-x;
bottom: 0;
height: rem-calc(24);
position: absolute;
width: 100%;
}
}
.divider {
display: none;
}
.more-info {
clear: both;
color: $text-medium;
font-size: $small-font-size;
a {
color: $text-medium;
}
}
.debate,
.debate-show,
.proposal-show,
.legislation-proposals {
.votes {
margin-top: $line-height;
padding: calc($line-height / 2) 0;
position: relative;
@include breakpoint(medium) {
margin-top: 0;
text-align: center;
}
.total-votes {
display: block;
font-weight: bold;
line-height: $line-height * 2;
}
}
}
.legislation-proposals {
.votes {
min-height: $line-height * 8;
}
}
.proposal,
.budget-investment {
.supports {
@include supports;
}
}
.budget-investment,
.budget-investment-show {
.supports {
.investment-project-amount {
color: $budget;
font-size: rem-calc(20);
font-weight: bold;
margin-bottom: 0;
}
.button-support {
@include background-with-text-contrast($budget);
font-size: $base-font-size;
font-weight: bold;
&:hover {
background: $budget-hover;
cursor: pointer;
}
&:active {
opacity: 0.75;
}
}
.total-supports {
color: $budget;
font-size: $base-font-size;
font-weight: bold;
}
.remove .icon-check-circle {
color: $budget;
display: block;
font-size: rem-calc(70);
line-height: rem-calc(70);
}
}
}
.budget-investment-show {
figure {
display: inline-block;
margin: rem-calc(10) 0 0;
figcaption {
font-size: $small-font-size;
margin-top: rem-calc(10);
}
}
.investment-external-link a {
word-wrap: break-word;
}
}
.budget-investment-show .supports {
border: 0;
}
.proposals-summary {
.panel {
min-height: 0;
}
}
.budget-investment .no-button,
.budget-investment-show .no-button {
display: block;
margin-top: $line-height * 1.5;
}
.budget-investment-show {
.label-budget-investment {
background: none;
clear: both;
color: $budget;
display: block;
font-size: rem-calc(12);
font-weight: bold;
line-height: $line-height;
padding-bottom: 0;
padding-left: 0;
padding-top: 0;
text-transform: uppercase;
}
.icon-budget {
color: $budget;
font-size: $small-font-size;
line-height: $line-height;
margin-left: rem-calc(6);
top: 0;
}
}
.help-header,
.section-header {
@include full-width-background;
@include full-width-border(bottom, 1px solid #eee);
background: #fafafa;
margin-bottom: $line-height;
margin-top: -$line-height;
padding-bottom: calc($line-height / 2);
padding-top: $line-height;
h1 {
font-size: rem-calc(24);
text-transform: uppercase;
}
}
.view-mode {
ul {
margin-bottom: 0;
li {
padding: calc($line-height / 4) 0;
padding-left: $line-height;
}
}
.dropdown-pane {
width: auto;
}
.button {
background: #eee;
height: rem-calc(36);
margin-bottom: 0;
padding: calc($line-height / 4);
width: rem-calc(36);
&::before {
color: color-pick-contrast(#eee);
font-family: "icons";
}
}
&.default {
.button {
&::before {
content: "\51";
}
}
}
&.minimal {
.button {
&::before {
content: "\22";
}
}
}
.view-list {
position: relative;
&::before {
content: "\22";
font-family: "icons";
left: 0;
position: absolute;
top: 6px;
}
}
.view-card {
position: relative;
&::before {
content: "\51";
font-family: "icons";
left: 0;
position: absolute;
top: 6px;
}
}
.is-active {
@include brand-color;
&::after {
content: "\6c";
font-family: "icons";
font-size: $tiny-font-size;
}
}
}
.debate,
.proposal,
.budget-investment {
&.minimal {
.panel,
.debate-content,
.proposal-content,
.budget-investment-content {
min-height: 0;
}
.panel h3 {
margin: 0 0 calc($line-height / 2);
@include breakpoint(medium) {
margin: 0;
}
}
}
}
// 05. Featured
// ------------
.featured-debates,
.featured-proposals {
padding: calc($line-height / 2) 0;
@include breakpoint(medium) {
margin-left: 0 !important;
margin-right: 0 !important;
}
h2 {
font-size: $small-font-size;
line-height: $line-height;
text-transform: uppercase;
}
h3 {
margin-bottom: 0;
a {
color: inherit;
font-size: rem-calc(24);
}
}
a,
.info {
color: color.adjust($body-font-color, $lightness: 15%);
font-size: $small-font-size;
}
}
.featured-debates {
background: $highlight;
}
.featured-proposals {
background: $featured;
.proposal-featured {
min-height: $line-height * 3.5;
}
.supports {
@include supports;
background: none;
border: 0;
padding-bottom: 0;
padding-top: 0;
&::after {
content: none;
}
.progress,
.total-supports {
display: none;
}
.button-support {
background: $body-font-color;
color: $featured;
margin-top: 0;
&:hover {
@extend %body-colors;
}
}
.participation-not-allowed {
background: $featured;
padding-top: 0;
a {
color: inherit;
}
p {
color: $color-warning;
font-size: $small-font-size;
line-height: $line-height;
}
}
.supported {
background: none;
border: 0;
font-size: $small-font-size;
margin-bottom: 0;
margin-top: 0;
padding: 0;
}
}
.share-supported {
.social-share-button .ssb-icon {
background: none;
color: inherit;
height: rem-calc(33);
&::before {
font-size: rem-calc(18);
line-height: rem-calc(33);
}
}
}
}
// 06. Budgets
// -----------
.budget-header {
@extend %budget-header;
margin-top: -$line-height;
min-height: $line-height * 25;
padding-bottom: $line-height;
padding-top: $line-height * 4;
&.with-background-image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
z-index: 0;
}
h1 {
padding-top: $line-height * 2;
}
.budget-title {
font-weight: bold;
text-transform: uppercase;
&::after {
border-bottom: 2px solid #fff;
content: "";
display: block;
margin: 0 auto;
padding-top: calc($line-height / 2);
width: 20%;
}
}
.confirmed {
font-size: rem-calc(24);
font-weight: bold;
}
.info {
background: #6a2a72;
p {
margin-bottom: 0;
text-transform: uppercase;
}
}
.main-link {
@include regular-button($color: rgba(0, 0, 0, 0.5));
font-size: 1.25rem;
margin-bottom: $line-height * 2;
min-width: 30%;
text-decoration: none;
}
}
.jumbo-budget {
@include full-width-border(bottom, 2px solid $border);
&.budget-heading {
min-height: $line-height * 10;
.check-ballot {
@include has-fa-icon(chevron-right, solid, after);
float: right;
min-width: rem-calc(240);
&::after {
margin-left: calc($line-height / 4);
}
}
}
h2 {
margin-bottom: 0;
}
.tagline {
display: block;
font-size: $small-font-size;
font-weight: normal;
}
.callout.warning {
font-size: $base-font-size;
a {
color: inherit;
}
}
a {
text-decoration: underline;
}
.button {
@extend %brand-background;
margin-bottom: rem-calc(3);
text-decoration: none;
}
}
.current-phase {
@include brand-color;
}
.progress-votes {
position: relative;
.progress {
background: #bbcdd9;
border-radius: rem-calc(12);
clear: both;
margin-bottom: 0;
overflow: hidden;
}
.progress-meter {
@include brand-background;
border-radius: rem-calc(12);
border-bottom-right-radius: 0;
border-top-right-radius: 0;
transition: width 2s;
}
.total-amount,
.amount-available {
font-weight: bold;
margin-bottom: 0;
text-transform: uppercase;
}
.total-amount {
font-size: $tiny-font-size;
@include breakpoint(medium) {
text-align: right;
}
span {
@include brand-color;
font-size: $base-font-size;
}
}
.amount-available {
display: block;
font-size: $small-font-size;
margin-top: calc($line-height / 8);
position: relative;
text-align: right;
white-space: nowrap;
&::before {
@include brand-color;
content: "\57";
font-family: "icons";
font-size: $small-font-size;
line-height: 0;
position: absolute;
right: -0.5em;
top: calc(-1 * $line-height / 8);
}
span {
@include brand-color;
font-size: rem-calc(24);
}
}
}
.ballot {
h2,
h3 {
font-weight: normal;
span {
color: $budget;
font-weight: bold;
}
}
.ballot-content {
border: 2px solid #f9f9f9;
border-radius: rem-calc(6);
padding: calc($line-height / 2);
}
.subtitle {
@include brand-border(left, 2px);
margin: calc($line-height / 2) 0;
padding-left: calc($line-height / 2);
}
.amount-spent {
background: $success-bg;
font-weight: normal;
padding: calc($line-height / 2);
span {
font-size: rem-calc(24);
font-weight: bold;
}
}
}
.ballot-list {
list-style: none;
margin-left: 0;
}
.select-district a {
display: inline-block;
margin: calc($line-height / 4) 0;
padding: calc($line-height / 4);
}
.select-district .is-active a {
background: #f9f9f9;
border-radius: rem-calc(3);
color: $budget;
font-weight: bold;
padding: calc($line-height / 4);
&::after {
content: "\56";
font-family: "icons";
font-size: $small-font-size;
font-weight: normal;
line-height: $line-height;
padding-left: rem-calc(3);
vertical-align: baseline;
&:hover {
text-decoration: none;
}
}
}
.progress-bar-nav {
position: relative;
z-index: 3;
@include breakpoint(medium) {
transition: height 0.3s;
&.is-fixed {
background: $body-background;
border-bottom: 2px solid $border;
height: auto;
left: 0;
padding: $line-height;
padding-bottom: calc($line-height / 2);
position: fixed;
top: 0;
width: 100%;
h2 {
font-size: rem-calc(24);
transition: font-size 0.3s;
}
}
}
h2 {
transition: font-size 0.3s;
@include breakpoint(small only) {
margin: $line-height 0;
}
}
}
.budgets-stats {
.header {
@include full-width-background;
background: $highlight;
}
}
// 07. Proposals successful
// -------------------------
.successful .panel {
.icon-successful {
border-bottom: 60px solid transparent;
border-right: 60px solid #ffd200;
border-top: 0;
height: 0;
position: absolute;
right: 0;
top: 0;
width: 0;
&::after {
color: #1b254c;
content: "\59";
font-family: "icons" !important;
left: 34px;
position: absolute;
top: 5px;
}
}
}
.successful {
.panel {
position: relative;
}
.truncate {
display: none;
}
.message {
@include supports;
background: none;
border-top: 0;
@include breakpoint(medium) {
border-left: 1px solid $border;
margin: $line-height rem-calc(-25) 0 rem-calc(12);
}
}
}
// 08. Polls
// ----------------------
.polls-show-header {
@include full-width-background;
background: #fafafa;
}
.poll-more-info,
.poll-more-info-options {
.read-more {
margin-bottom: $line-height;
margin-top: $line-height;
button {
@include link;
}
}
}
.poll-more-info {
border-top: 1px solid #eee;
}
.poll-more-info-options {
@include full-width-background;
@include full-width-border(top, 1px solid #eee);
@include full-width-border(bottom, 1px solid #eee);
background: #fafafa;
.column:nth-child(odd) {
border-right: 2px solid;
}
.option-divider {
border-bottom: 2px solid;
border-right: 0 !important;
margin-bottom: $line-height;
padding-bottom: $line-height;
}
.option-description {
height: 100%;
&.short {
height: rem-calc(300);
overflow: hidden;
}
}
.document-link {
> :first-child {
@include has-fa-icon(file, regular);
&::before {
position: relative;
top: -0.1rem;
}
}
a {
word-wrap: break-word;
}
}
}
.orbit-bullets button {
background-color: #ccc;
height: calc($line-height / 2);
width: calc($line-height / 2);
&.is-active {
@include brand-background;
}
}
.orbit-container {
height: 100% !important;
max-height: none !important;
li {
margin-bottom: 0 !important;
}
}
.orbit-slide {
max-height: none !important;
position: relative;
}
.orbit-next,
.orbit-previous {
background: rgba(34, 34, 34, 0.25);
}
.zoom-link {
@extend %body-colors;
border-radius: rem-calc(48);
cursor: pointer;
font-size: rem-calc(24);
font-weight: bold;
height: rem-calc(48);
line-height: rem-calc(48);
padding-top: rem-calc(4);
position: absolute;
right: 12px;
text-align: center;
top: 24px;
width: rem-calc(48);
z-index: 9;
&:hover {
@include brand-secondary-background;
text-decoration: none;
}
}
.image-container {
background: #fafafa;
overflow: hidden;
position: relative;
}
.public .poll {
border: 1px solid $border;
margin-bottom: calc($line-height / 2);
padding: calc($line-height / 2);
position: relative;
}
.section-title-divider {
border-bottom: 1px solid #eee;
margin: $line-height 0;
span {
border-bottom: 1px solid;
}
}
// 09. Polls results and stats
// ---------------------------
.polls-results-stats {
table {
table-layout: fixed;
caption {
padding: calc($line-height / 2) 0;
text-align: left;
}
th {
text-align: left;
&.win {
background: #009fde;
}
}
td {
&.win {
background: #ccedf8;
font-weight: bold;
}
}
}
}