]> git.openstreetmap.org Git - rails.git/blob - app/assets/stylesheets/common.scss
Replace Leaflet contextmenu plugin w/ Bootstrap
[rails.git] / app / assets / stylesheets / common.scss
1 @use "sass:map";
2 @import "parameters";
3 @import "bootstrap";
4 @import "rails_bootstrap_forms";
5 @import "colors";
6
7 $bootstrap-icons-font-dir: "bootstrap-icons/font/fonts";
8 @import "bootstrap-icons/font/bootstrap-icons";
9
10 /* Styles common to large and small screens */
11
12 /* Default rules for the body of every page */
13
14 body {
15   font-size: $typeheight;
16   --dark-mode-map-filter: none;
17 }
18
19 time[title] {
20   text-decoration: underline dotted;
21 }
22
23 /* Utility for de-emphasizing content */
24
25 .text-body-secondary a {
26   color: $blue;
27 }
28
29 /* Bootstrap contextual table classes overrides in dark mode */
30
31 @include color-mode(dark) {
32   .table-primary {
33     --bs-table-bg: rgb(var(--bs-primary-rgb), .25);
34   }
35   .table-secondary {
36     --bs-table-bg: rgb(var(--bs-secondary-rgb), .25);
37   }
38   .table-success {
39     --bs-table-bg: rgb(var(--bs-success-rgb), .25);
40   }
41   .table-primary, .table-secondary, .table-success {
42     --bs-table-color: initial;
43     border-color: inherit;
44   }
45 }
46
47 /* Utility for delayed loading spinner */
48
49 .delayed-fade-in {
50   animation: 300ms linear forwards delayed-fade-in;
51 }
52
53 @keyframes delayed-fade-in {
54   0%   { opacity: 0 }
55   66%  { opacity: 0 }
56   100% { opacity: 1 }
57 }
58
59 /* Bootstrap close button overrides for nested light/dark themes */
60
61 [data-bs-theme="dark"],
62 [data-bs-theme] [data-bs-theme="dark"] {
63   --bs-btn-close-filter: invert(1) grayscale(100%) brightness(200%);
64 }
65
66 [data-bs-theme="light"],
67 [data-bs-theme] [data-bs-theme="light"] {
68   --bs-btn-close-filter: none;
69 }
70
71 /* Rules for the header */
72
73 #menu-icon svg {
74   fill: var(--bs-emphasis-color);
75   stroke: var(--bs-body-bg);
76   opacity: 0.6;
77 }
78
79 header {
80   height: $headerHeight;
81   position: relative;
82   font-size: 14px;
83
84   > * {
85     padding: $lineheight * 0.5;
86   }
87
88   h1 {
89     height: $headerHeight;
90     font-size: 18px;
91     gap: $lineheight * 0.5;
92   }
93
94   .btn {
95     font-size: 14px;
96   }
97
98   .username {
99     max-width: 12em;
100   }
101 }
102
103 nav.primary {
104   #edit_tab .btn-outline-primary {
105     @include button-outline-variant($green, $color-hover: $white, $active-color: $white);
106   }
107
108   .disabled {
109     .btn-outline-primary {
110       color: $grey;
111       cursor: default;
112
113       &:hover {
114         background-color: lighten($green, 30%);
115       }
116     }
117   }
118
119   // Small tweaks to the toggle to stop the primary colour showing through
120   // when the menu is shown
121   .show > .btn-outline-primary.dropdown-toggle {
122     background-color: $green;
123     border-color: $green;
124
125     &:focus {
126       box-shadow: 0 0 0 0.2rem fade-out($green, 0.5);
127     }
128   }
129 }
130
131 nav.secondary {
132   .nav-link {
133     padding: 0 0.3rem;
134   }
135
136   > ul {
137     height: 1.5em;
138   }
139 }
140
141 nav.primary, nav.secondary {
142   .dropdown-item {
143     &:hover, &:active {
144       background-color: $green;
145       color: white;
146     }
147   }
148 }
149
150 #compact-secondary-nav {
151   display: none;
152 }
153
154 body.small-nav {
155   header {
156     height: auto;
157     min-height: $headerHeight;
158
159     &.closed nav {
160       display: none !important;
161     }
162
163     .username {
164       max-width: unset;
165     }
166   }
167
168   nav.primary {
169     padding: 0;
170
171     #edit_tab {
172       padding: 10px;
173     }
174   }
175
176   nav.secondary {
177     > ul {
178       height: auto;
179     }
180
181     .user-menu, .login-menu {
182       width: 100%;
183     }
184   }
185
186   #compact-secondary-nav {
187     display: none;
188   }
189
190   .overlay-sidebar #sidebar {
191     .welcome,
192     #banner {
193       display: none;
194     }
195   }
196 }
197
198 /* Rules for language selector */
199
200 .select_language_list {
201   column-width: 165px;
202
203   a {
204     .native_name small {
205       font-size: 10px;
206     }
207
208     &:hover .current_locale_name {
209       text-decoration: underline;
210     }
211   }
212 }
213
214 /* Utility for styling notification numbers */
215
216 .count-number {
217   background: transparentize(lighten($green, 25%), .25);
218   color: $gray-800;
219   font-weight: $font-weight-normal;
220 }
221
222 /* Rules for Leaflet maps */
223
224 .leaflet-top.leaflet-right,
225 .leaflet-top.leaflet-left {
226   height: 100%;
227   column-gap: 10px;
228   display: flex;
229   flex-direction: column;
230   flex-wrap: wrap-reverse;
231 }
232
233 .leaflet-control .control-button {
234   display: block;
235   height: 40px;
236   width: 40px;
237   color: white;
238   padding: 10px;
239   background-color: rgba(0,0,0,.6);
240   outline: none;
241
242   &:hover,
243   &:focus {
244     background-color: black;
245   }
246
247   &.disabled,
248   &.leaflet-disabled {
249     background-color: rgba(0,0,0,.5);
250     cursor: default;
251   }
252
253   &-first {
254     border-start-start-radius: 4px;
255   }
256
257   &-last {
258     border-end-start-radius: 4px;
259     margin-bottom: 10px;
260   }
261 }
262
263 .leaflet-control.active .control-button {
264   background-color: $vibrant-green;
265 }
266
267 /* Rules for the sidebar and main map area */
268
269 .map-layout {
270   #content {
271     overflow: hidden;
272     position: absolute;
273     top: $headerHeight;
274     bottom: 0;
275     width: 100%;
276   }
277
278   #sidebar, #map {
279     position: relative;
280     height: 100%;
281     overflow-x: hidden;
282     overflow-y: auto;
283   }
284
285   #sidebar {
286     float: left;
287     width: $sidebarWidth;
288   }
289
290   .overlay-sidebar #sidebar {
291     position: absolute;
292     height: auto;
293     overflow: hidden;
294
295     .welcome,
296     #banner {
297       display: block;
298     }
299
300     .sidebar-close-controls,
301     #sidebar_loader,
302     #sidebar_content {
303       display: none;
304     }
305   }
306
307   .welcome,
308   #banner {
309     display: none;
310   }
311
312   #banner img {
313     display: block;
314     width: $sidebarWidth;
315   }
316
317   #map {
318     height: 100%;
319     overflow: hidden;
320
321     &.query-active {
322       cursor: help;
323     }
324
325     &.query-disabled {
326       cursor: not-allowed;
327     }
328
329     .leaflet-marker-draggable {
330       cursor: move;
331     }
332
333     .query-marker {
334       animation: 1500ms forwards query-marker-fade;
335
336       @keyframes query-marker-fade {
337         to { opacity: 0 }
338       }
339     }
340   }
341
342   #map-ui {
343     display: none;
344     position: relative;
345     float: right;
346     width: 250px;
347     height: 100%;
348     overflow: auto;
349   }
350 }
351
352 @include media-breakpoint-down(md) {
353   body.map-layout {
354     #sidebar, #map {
355       position: relative;
356       overflow-x: hidden;
357       width: 100%;
358       height: 50%;
359     }
360
361     #map-ui {
362       width: 100%;
363       height: 50%;
364       overflow-y: scroll;
365     }
366
367     .overlay-sidebar.overlay-right-sidebar {
368       #sidebar {
369         position: absolute;
370         width: 350px;
371         height: auto;
372         overflow: hidden;
373       }
374
375       #map {
376         height: 100%;
377       }
378     }
379   }
380 }
381
382 .layers-ui {
383   .base-layers > * {
384     height: 3.5rem;
385
386     > .btn {
387       box-sizing: content-box;
388       top: - map.get($border-widths, 4);
389       left: - map.get($border-widths, 4);
390       --bs-btn-border-color: var(--bs-body-bg);
391     }
392     > .btn:hover {
393       --bs-btn-border-color: var(--bs-primary-border-subtle);
394     }
395   }
396
397   .overlay-layers {
398     .form-check.disabled { color: $darkgrey; }
399   }
400 }
401
402 .share-ui {
403   #mapnik_scale {
404     width: 100px;
405   }
406 }
407
408 .leaflet-top {
409   top: 10px !important;
410   .leaflet-control {
411     margin-right: 0px !important;
412     margin-top: 0px !important;
413   }
414 }
415
416 .leaflet-popup-scrolled {
417   padding-right: $lineheight;
418   border-bottom: 0px !important;
419   border-top: 0px !important;
420 }
421
422 .leaflet-popup-content-wrapper, .leaflet-popup-tip,
423 .leaflet-contextmenu, .leaflet-contextmenu-item,
424 .leaflet-control-attribution, .leaflet-control-scale-line {
425   @extend .bg-body, .text-body;
426 }
427
428 .leaflet-control-attribution, .leaflet-control-scale-line {
429   @extend .bg-opacity-75;
430   text-shadow: none !important;
431 }
432
433 .leaflet-contextmenu-item.over {
434   @extend .bg-body-secondary, .border-secondary, .border-opacity-10;
435 }
436
437 .leaflet-popup-content-wrapper {
438   @extend .rounded-1;
439
440   a {
441     color: var(--bs-link-color) !important;
442   }
443 }
444
445 @include color-mode(dark) {
446   .leaflet-container .leaflet-control-attribution a {
447     color: var(--bs-link-color);
448   }
449
450   .leaflet-control-scale-line {
451     border-color: rgba(var(--bs-light-rgb), .75) !important;
452   }
453 }
454
455 @mixin dark-map-color-scheme {
456   .leaflet-tile-container,
457   #legend .filtered-image {
458     filter: var(--dark-mode-map-filter);
459   }
460
461   .leaflet-tile-container .leaflet-tile {
462     filter: none;
463   }
464 }
465
466 body[data-map-theme="dark"] {
467   @include dark-map-color-scheme;
468 }
469
470 @include color-mode(dark) {
471   body:not([data-map-theme]) {
472     @include dark-map-color-scheme;
473   }
474 }
475
476 /* Rules for attribution text under the main map shown on printouts */
477
478 .donate-attr { color: darken($green, 10%) !important; }
479
480 /* Temporary label size override until we remove site-wide font customisation */
481
482 form {
483   label {
484     font-size: 16px;
485   }
486   .col-form-label {
487     font-size: 16px;
488   }
489 }
490
491 /* Stop bootstrap 5 from floating legends when they don't need to be */
492 legend {
493   float: none;
494 }
495
496 /* Override the text colour for primary and secondary buttons, to match our
497    bootstrap 4 colours. Note this has accessibility issues, which is why
498    bootstrap 5 calculates black as the appropriate colour, and we should
499    reconsider our colours at some point with that in mind. */
500
501 .btn-primary {
502   @include button-variant($primary, $primary, $color: $white, $hover-color: $white, $active-color: $white, $disabled-color: $white);
503 }
504
505 .btn-secondary {
506   @include button-variant($secondary, $secondary, $color: $white, $hover-color: $white, $active-color: $white, $disabled-color: $white);
507 }
508
509 .btn-outline-secondary {
510   @include button-outline-variant($secondary, $color-hover: $white, $active-color: $white);
511 }
512
513 /* Rules for the search and direction forms */
514
515 .directions_form {
516   display: none;
517 }
518
519 .search_form {
520   .describe_location {
521     font-size: 10px;
522   }
523
524   input:not(:placeholder-shown) + .input-group-text .describe_location {
525     display: none;
526   }
527 }
528
529 /* Rules for search results */
530
531 .search_results_entry li.list-group-item {
532   border-right: 1em solid var(--marker-color);
533 }
534
535 .leaflet-marker-icon.activatable:is(.active, :hover) > svg {
536   transform: scale(1.5);
537   transform-origin: bottom;
538 }
539
540 /* Rules for routing */
541
542 td.distance {
543     font-size: x-small;
544 }
545 tr.turn {
546     cursor: pointer;
547 }
548
549 .routing_marker_column {
550   margin-left: .35rem;
551   margin-right: .35rem;
552   width: 15px;
553
554   svg {
555     cursor: move;
556   }
557 }
558
559 /* Rules for the history sidebar */
560
561 .changeset-above-sidebar-viewport {
562   --changeset-border-color: #CC6655;
563   --changeset-fill-color: #DDBBBB;
564   --changeset-outline-color: #FFF4F4;
565 }
566 .changeset-in-sidebar-viewport {
567   --changeset-border-color: #FF9500;
568   --changeset-fill-color: #FFFFAF;
569   --changeset-outline-color: #FFFFFF;
570   &.changeset-highlight-outline {
571     filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, .75));
572   }
573 }
574 .changeset-below-sidebar-viewport {
575   --changeset-border-color: #8888AA;
576   --changeset-fill-color: #CCCCDD;
577   --changeset-outline-color: #F4F4FF;
578 }
579 .changeset-highlight-outline {
580   filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, .25));
581 }
582
583 #sidebar .changesets {
584   .changeset-color-hint-bar {
585     height: 2px;
586     background: var(--changeset-border-color);
587   }
588
589   li {
590     &.selected {
591       @extend :hover;
592     }
593
594     a.stretched-link > bdi, a:not(.stretched-link), [title] {
595       position: relative;
596       z-index: 2; /* needs to be higher than Bootstrap's stretched link ::after z-index */
597     }
598   }
599 }
600
601 #sidebar .changeset_line .changeset_num_comments {
602   min-width: 2.5em;
603 }
604
605 /* Rules for the browse sidebar */
606
607 #sidebar_content {
608   .browse-tag-list {
609     table-layout: fixed;
610
611     tr > *:not([colspan]) {
612       white-space: pre-wrap;
613       word-wrap: break-word;
614       word-break: break-word;
615     }
616
617     tr:last-child > * {
618       border-bottom: 0;
619     }
620   }
621
622   .browse-element-list {
623     line-height: 1.25rem;
624
625     .browse-icon {
626       height: 1.25rem;
627     }
628
629     .d-flex > .browse-icon {
630       height: max(20px, 1.25rem);
631     }
632   }
633
634   .query-results {
635     display: none;
636   }
637 }
638
639 @include color-mode(dark) {
640   #sidebar_content .browse-element-list .browse-icon-invertible {
641     filter: invert(.8) hue-rotate(180deg);
642   }
643 }
644
645 /* Force LTR/RTL alignment for placeholder text */
646
647 .form-control::placeholder {
648   text-align: left;
649 }
650
651 /* Rules for export sidebar */
652
653 .export_form {
654   .export_area_inputs {
655     input[type="text"] {
656       width: 100px;
657     }
658   }
659
660   .export_boxy {
661     > * {
662         margin: -1px;
663     }
664   }
665 }
666
667 /* Rules for edit pages */
668
669 .site-edit {
670   #content {
671     position: absolute;
672     top: $headerHeight;
673     bottom: 0;
674     width: 100%;
675   }
676 }
677
678 /* Rules for non-map content pages */
679
680 .content-inner {
681   max-width: 960px;
682   padding: $lineheight;
683 }
684
685 /* Rules for login and signup pages */
686
687 .sessions-new, .users-new, .users-create {
688   #content .content-inner {
689     max-width: 760px;
690   }
691 }
692
693 .header-illustration {
694   background-position: right;
695   background-repeat: no-repeat;
696   position: relative;
697   min-height: 200px;
698   width: 100%;
699   left: 0;
700   bottom: 0;
701
702   &.new-user-main {
703     background-image: image-url("sign-up-illustration.svg");
704     background-position-x: 70px;
705   }
706
707   &.confirm-main {
708     background-image: image-url("confirm-illustration.svg");
709   }
710
711   &.new-user-terms {
712     background-image: image-url("terms-illustration.svg");
713   }
714 }
715
716 [dir=rtl] .header-illustration {
717   transform: scaleX(-1);
718
719   h1 {
720     transform: scaleX(-1);
721   }
722
723   ul {
724     transform: scaleX(-1);
725   }
726 }
727
728 /* Rules for small maps in content areas */
729
730 .content_map {
731   height: 200px;
732   margin-bottom: $lineheight;
733 }
734
735 @include media-breakpoint-up(md) {
736   .content_map {
737     height: 400px;
738   }
739 }
740
741 /* Rules for the user map */
742
743 .content_map .leaflet-popup-content {
744   margin: $spacer;
745 }
746
747 /* Rules for user popups on maps */
748
749 .user_popup {
750   p {
751     margin: 0 0 5px 0 !important;
752     font-size: 12px;
753   }
754 }
755
756 /* Rules for the diary entry page */
757
758 .diary_entries {
759   #map {
760     height: 400px;
761     display: none;
762   }
763   .diary-comment .col-auto {
764     width: 62px;
765   }
766   .diary-comment .col {
767     max-width: 690px;
768   }
769 }
770
771 /* Rules for the issues page */
772
773 .issues.issues-index {
774   td.reporting_users {
775     max-width: 5rem;
776   }
777 }
778
779 /* Rules for the account confirmation page */
780
781 .accounts-terms-show {
782   .legale {
783     padding: $lineheight;
784     margin-bottom: $lineheight;
785     overflow: auto;
786     height: 20em;
787
788     li {
789       list-style: inherit;
790     }
791
792     ol ol {
793       list-style-type: lower-alpha;
794     }
795   }
796 }
797
798 /* Rules for user images */
799
800 img.user_image {
801   max-width: 100px;
802   max-height: 100px;
803 }
804
805 img.user_thumbnail {
806   max-width: 50px;
807   max-height: 50px;
808 }
809
810 img.user_thumbnail_tiny {
811   width: 25px;
812   height: 25px;
813   object-fit: contain;
814 }
815
816 /* General styles for action lists / subnavs */
817
818 nav.secondary-actions {
819   margin-left: -11px;
820   overflow: hidden;
821   > ul {
822     display: flex;
823     flex-direction: row;
824     flex-wrap: wrap;
825     margin-bottom: 0;
826     margin-left: -1px;
827     padding: 0;
828     > li {
829       flex-basis: auto;
830       list-style: none;
831       border-left: 1px solid $grey;
832       padding-left: $lineheight * 0.5;
833       margin-right: $lineheight * 0.5;
834       margin-bottom: $lineheight * 0.125;
835     }
836   }
837 }
838
839 div.secondary-actions {
840   padding: 10px;
841   text-align: center;
842 }
843
844 /* Rules for rich text */
845
846 .richtext {
847   code {
848     background: var(--bs-secondary-bg);
849     padding: 2px 3px;
850   }
851
852   pre {
853     background: var(--bs-secondary-bg);
854     padding: 2px 3px;
855     white-space: pre-wrap;
856
857     code {
858       padding: 0;
859     }
860   }
861
862   img {
863     padding: $lineheight;
864     background-color: var(--bs-tertiary-bg);
865     display: block;
866     max-width: 100%;
867     margin: auto;
868   }
869
870   blockquote {
871     border-left: $lineheight solid var(--bs-tertiary-bg);
872     padding-left: $lineheight;
873     margin: 0;
874     color: var(--bs-secondary-color);
875   }
876 }
877
878 /* Rules for the "About" page */
879
880 .site-about #content {
881   .content-inner {
882     max-width: 760px;
883   }
884
885   .attr {
886     margin-top: -20px;
887
888     h1 {
889       span {
890         color: $vibrant-green;
891       }
892     }
893
894     .user-image {
895       height: 150px;
896       background-position: 0 50%;
897       background-repeat: no-repeat;
898       background-image: image-url('about/osm.png');
899       background-size: cover;
900       background-color: $vibrant-green;
901     }
902
903     .byosm {
904       background: $vibrant-green;
905     }
906
907     .byosm span {
908       display: inline-block;
909       width: 1em;
910       margin-left: -1em;
911     }
912   }
913 }
914
915 /* Rules for tables with usernames */
916
917 .messages-table .username,
918 #block_list .username {
919   max-width: 20em;
920 }
921
922 /* Rules for navigation tabs */
923
924 .nav-tabs .username {
925   max-width: 20em;
926 }
927
928 .bg-body-secondary .nav-tabs {
929   --bs-border-color: var(--bs-secondary-border-subtle);
930   --bs-secondary-bg: var(--bs-secondary-border-subtle);
931   margin-bottom: -1px;
932 }
933
934 /* Rules for traces */
935
936 img.trace_image {
937   mix-blend-mode: darken;
938 }
939
940 @include color-mode(dark) {
941   img.trace_image {
942     filter: invert(1);
943     mix-blend-mode: lighten;
944   }
945 }
946
947 /* Rules for the heatmap */
948
949 .heatmap {
950   grid-template-columns: auto;
951   grid-auto-columns: minmax(1em, 1fr);
952   grid-template-rows: auto;
953   grid-auto-rows: minmax(1em, 1fr);
954   font-size: x-small;
955   gap: 0.3em;
956
957   [data-date], [data-date] span {
958     display: block;
959     aspect-ratio: 1;
960     border-radius: 25%;
961   }
962
963   [data-date] {
964     background-color: #ededed;
965     span {
966       background-color: #14432a;
967     }
968     &[data-count] {
969       background-color: #4dd05a;
970     }
971     &:hover {
972       box-shadow: 0px 0px 0px 1px #8884;
973     }
974   }
975 }
976
977 @include color-mode(dark) {
978   .heatmap {
979     [data-date] {
980       background-color: #2d333b;
981       span {
982         background-color: #4dd05a;
983       }
984       &[data-count] {
985         background-color: #14432a;
986       }
987     }
988   }
989 }
990
991 .tooltip.wide {
992   --bs-tooltip-max-width: none;
993 }
994
995 /* Rules for auth provider logos */
996
997 .apple-logo rect { fill: black; }
998 .apple-logo path { fill: white; }
999
1000 @media (prefers-color-scheme: dark) {
1001   .apple-logo rect { fill: white; }
1002   .apple-logo path { fill: black; }
1003 }
1004
1005 .github-logo rect { fill: white; }
1006 .github-logo path { fill: black; }
1007
1008 @media (prefers-color-scheme: dark) {
1009   .github-logo rect { fill: black; }
1010   .github-logo path { fill: white; }
1011 }
1012
1013 /* Rules for contextmenu */
1014
1015 #map-context-menu {
1016   .dropdown-item {
1017     &:hover, &:active {
1018       background-color: $gray-200;
1019     }
1020   }
1021   &.cm_dropdown {
1022     position: absolute;
1023     z-index: 1000;
1024   }
1025   &.cm_dropdown_menu {
1026     position: absolute;
1027     display: block;
1028     min-width: 200px;
1029   }
1030 }
1031
1032 @media (prefers-color-scheme: dark) {
1033   #map-context-menu {
1034     .dropdown-item {
1035       &:hover, &:active {
1036         background-color: $gray-700;
1037       }
1038     }
1039   }
1040 }