mirror of
https://github.com/itflow-org/itflow
synced 2026-08-27 01:45:13 +00:00
559 lines
17 KiB
CSS
559 lines
17 KiB
CSS
@import "flag-offsets.css";
|
|
|
|
:root {
|
|
/* rgba is needed for the selected country hover state to blend in with the border-highlighting some browsers give the input on focus.
|
|
Used as the bg color for both the selected country and the country list items, on hover. */
|
|
--iti-hover-color: rgba(0, 0, 0, 0.05);
|
|
/* Used for both the country selector border, and the divider under the country search input. */
|
|
--iti-border-color: #ccc;
|
|
--iti-country-selector-bg: white;
|
|
/* icons include: arrow icon, globe icon, search icon, clear icon, check icon */
|
|
--iti-icon-color: #555;
|
|
/* horizontal spacing around the flag, country name, search icon, clear icon, check icon etc */
|
|
--iti-spacer-horizontal: 10px;
|
|
/* Flags are a 4:3 aspect ratio, so height is derived from width — consumers only need to override --iti-flag-width to scale.
|
|
--iti-flag-width itself is set in flag-offsets.css (auto-generated). */
|
|
--iti-flag-height: calc(var(--iti-flag-width) * 3 / 4);
|
|
/* Separate height for the globe icon, as it's much thinner than the flags, so nice to have it a bit taller. This is also re-used for the search icon. */
|
|
--iti-globe-icon-size: 17px;
|
|
/* Separate height for the search clear icon, which looks too big if it's the same as the others. */
|
|
--iti-clear-icon-size: 15px;
|
|
/* This border width is used for the popup and divider, but it is also assumed to be the border width of the input, which we do not control. */
|
|
--iti-border-width: 1px;
|
|
/* Arrow */
|
|
--iti-arrow-size: 5px;
|
|
--iti-arrow-width: 1.5px; /* thickness */
|
|
--iti-arrow-padding: 7px;
|
|
|
|
/* NOTE: image paths are now defined in intlTelInputWithAssets.css */
|
|
|
|
/* Enough space for them to click off to close. */
|
|
--iti-mobile-popup-margin: 30px;
|
|
|
|
/* Colour used for the flash variant of the strictRejectAnimation option (shown when the user has prefers-reduced-motion). */
|
|
--iti-strict-reject-flash-color: rgba(255, 0, 0, 0.12);
|
|
}
|
|
|
|
.iti {
|
|
/* We need position on the container so the selected country can be absolutely positioned over the input. */
|
|
position: relative;
|
|
/* Keep the input's default inline properties. */
|
|
display: inline-block;
|
|
|
|
/* Paul Irish (paulirish) says this is ok.
|
|
http://www.paulirish.com/2012/box-sizing-border-box-ftw/ */
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* Specify types to increase specificity e.g. to override bootstrap v2.3. */
|
|
input.iti__tel-input,
|
|
input.iti__tel-input[type="text"],
|
|
input.iti__tel-input[type="tel"] {
|
|
position: relative;
|
|
/* Input is bottom level, below selected country and country selector. */
|
|
z-index: 0;
|
|
|
|
/* By default the input is inline, which means any inherited line-height will make the wrapper (and hence the selected country) taller than the input. Block is a better default in this case, given it should fill the wrapper. */
|
|
display: block;
|
|
|
|
/* Ensure the input fills the wrapper e.g. when the wrapper is inside a flex/grid container.
|
|
When the wrapper has no explicit width (standard inline-block), this is a cyclic percentage
|
|
and resolves to auto per CSS Sizing 3, so it has no effect and any consumer-set width wins. */
|
|
min-width: 100%;
|
|
|
|
/* Since we wrap the input in a container div, any margin here would interfere with the positioning of the selected country. */
|
|
margin: 0 !important;
|
|
}
|
|
}
|
|
|
|
.iti__a11y-text {
|
|
width: 1px;
|
|
height: 1px;
|
|
clip: rect(1px, 1px, 1px, 1px);
|
|
overflow: hidden;
|
|
position: absolute;
|
|
}
|
|
|
|
.iti__country-container {
|
|
/* Positioned over the top of the input. */
|
|
position: absolute;
|
|
/* Full height. */
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
/* Prevent the highlighted child from overlapping the input border. */
|
|
padding: var(--iti-border-width);
|
|
}
|
|
|
|
/* Now a button element. */
|
|
.iti__selected-country {
|
|
/* Render above the input. */
|
|
z-index: 1;
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
/* This must be full-height both for the hover highlight, and to push down the dropdown so it appears below the input. */
|
|
height: 100%;
|
|
|
|
/* Reset button styles (can't use all:unset as lose browser default focus outline styles). */
|
|
background: none;
|
|
border: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: inherit;
|
|
font-size: inherit;
|
|
color: inherit;
|
|
border-radius: 0;
|
|
font-weight: inherit;
|
|
line-height: inherit;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.iti__selected-country-primary {
|
|
display: flex;
|
|
align-items: center;
|
|
/* This must be full-height for the hover highlight. */
|
|
height: 100%;
|
|
padding: 0 var(--iti-arrow-padding) 0 var(--iti-spacer-horizontal);
|
|
}
|
|
|
|
.iti__selected-dial-code {
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.iti__arrow {
|
|
margin-left: var(--iti-arrow-padding);
|
|
margin-top: -2px;
|
|
|
|
width: var(--iti-arrow-size);
|
|
height: var(--iti-arrow-size);
|
|
box-sizing: border-box;
|
|
border-right: var(--iti-arrow-width) solid var(--iti-icon-color);
|
|
border-bottom: var(--iti-arrow-width) solid var(--iti-icon-color);
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.iti__arrow--up {
|
|
margin-top: 4px;
|
|
transform: rotate(-135deg);
|
|
}
|
|
|
|
/* The country selector. */
|
|
.iti__country-selector {
|
|
border-radius: 3px;
|
|
background-color: var(--iti-country-selector-bg);
|
|
|
|
.iti--inline-country-selector & {
|
|
border: var(--iti-border-width) solid var(--iti-border-color);
|
|
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2);
|
|
}
|
|
/* Note: don't apply these styles when using dropdownParent, as that uses pos:fixed and so this (child) doesn't need pos:absolute */
|
|
.iti--inline-country-selector:not(.iti--detached-country-selector) & {
|
|
position: absolute;
|
|
/* Popup so render above everything else. */
|
|
z-index: 2;
|
|
left: 0;
|
|
}
|
|
}
|
|
|
|
.iti__search-input {
|
|
width: 100%;
|
|
border-width: 0;
|
|
border-radius: 3px;
|
|
/* Space for search icon. */
|
|
padding-left: calc(
|
|
var(--iti-spacer-horizontal) + var(--iti-globe-icon-size) +
|
|
var(--iti-spacer-horizontal)
|
|
);
|
|
/* Space for clear button. */
|
|
padding-right: calc(
|
|
var(--iti-spacer-horizontal) + var(--iti-clear-icon-size) +
|
|
var(--iti-spacer-horizontal)
|
|
);
|
|
|
|
/* RTL: put the icon on the right instead. */
|
|
[dir="rtl"] & {
|
|
padding-left: inherit;
|
|
padding-right: 30px;
|
|
background-position: right 8px center;
|
|
}
|
|
|
|
/* hide chrome/safari's native clear button for inputs with type=search */
|
|
&::-webkit-search-cancel-button {
|
|
appearance: none;
|
|
}
|
|
}
|
|
|
|
/* give the search input the same v-padding as the country items, for visual consistency */
|
|
.iti__search-input,
|
|
.iti__country {
|
|
padding-top: 8px;
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
.iti__search-input-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
border-bottom: 1px solid var(--iti-border-color);
|
|
}
|
|
|
|
.iti__search-icon {
|
|
position: absolute;
|
|
left: var(--iti-spacer-horizontal);
|
|
display: flex;
|
|
pointer-events: none;
|
|
|
|
[dir="rtl"] & {
|
|
left: auto;
|
|
right: var(--iti-spacer-horizontal);
|
|
}
|
|
}
|
|
|
|
.iti__search-icon-svg {
|
|
width: var(--iti-globe-icon-size);
|
|
height: var(--iti-globe-icon-size);
|
|
display: block;
|
|
stroke: var(--iti-icon-color);
|
|
fill: none;
|
|
stroke-width: 3;
|
|
}
|
|
|
|
.iti__search-clear {
|
|
position: absolute;
|
|
/* half the spacer value as the other half is in the padding */
|
|
right: calc(var(--iti-spacer-horizontal) / 2);
|
|
background: transparent;
|
|
border: 0;
|
|
border-radius: 3px; /* nice on hover state */
|
|
cursor: pointer;
|
|
padding: calc(var(--iti-spacer-horizontal) / 2);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: background-color 0.15s ease;
|
|
|
|
.iti__search-clear-x {
|
|
stroke-width: 2;
|
|
}
|
|
.iti__search-clear-bg {
|
|
fill: var(--iti-icon-color);
|
|
}
|
|
|
|
[dir="rtl"] & {
|
|
right: auto;
|
|
left: var(--iti-spacer-horizontal);
|
|
}
|
|
|
|
&:hover,
|
|
&:focus-visible {
|
|
background: var(--iti-hover-color);
|
|
outline: none;
|
|
}
|
|
}
|
|
|
|
.iti__search-clear-svg {
|
|
width: var(--iti-clear-icon-size);
|
|
height: var(--iti-clear-icon-size);
|
|
display: block;
|
|
}
|
|
|
|
.iti__no-results {
|
|
text-align: center;
|
|
padding: 30px 0;
|
|
}
|
|
|
|
.iti__country-list {
|
|
/* Override default list styles. */
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
cursor: pointer;
|
|
|
|
overflow-y: scroll;
|
|
/* Fixes https://github.com/jackocnr/intl-tel-input/issues/765
|
|
Apple still hasn't fixed the issue where setting overflow: scroll on a div element does not use inertia scrolling
|
|
If this is not set, then the country list scroll stops moving after raising a finger, and users report that scroll is slow
|
|
Stackoverflow question about it: https://stackoverflow.com/questions/33601165/scrolling-slow-on-mobile-ios-when-using-overflowscroll */
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
.iti--inline-country-selector & {
|
|
max-height: 185px;
|
|
}
|
|
}
|
|
|
|
.iti--flexible-dropdown-width .iti__country-list {
|
|
/* Don't let the contents wrap AKA the container will be as wide as the contents. */
|
|
white-space: nowrap;
|
|
|
|
/* Except on small screens, where we force the dropdown width to match the input. */
|
|
@media (max-width: 500px) {
|
|
white-space: normal;
|
|
}
|
|
}
|
|
|
|
/* Each country item in the list (we must have separate class to differentiate from dividers). */
|
|
.iti__country {
|
|
/* Note: decided not to use line-height here for alignment because it causes issues e.g. large font-sizes will overlap, and also looks bad if one country overflows onto 2 lines. */
|
|
display: flex;
|
|
align-items: center;
|
|
padding-left: var(--iti-spacer-horizontal);
|
|
padding-right: var(--iti-spacer-horizontal);
|
|
outline: none;
|
|
|
|
&.iti__highlight {
|
|
background-color: var(--iti-hover-color);
|
|
}
|
|
}
|
|
|
|
.iti__country-name {
|
|
/* expand to fill available space, so the last flex item, the check mark, is pushed to the right edge */
|
|
flex-grow: 1;
|
|
}
|
|
|
|
/* Check icon shown on the right for the selected country. */
|
|
.iti__country-check {
|
|
margin-left: var(--iti-spacer-horizontal);
|
|
display: flex;
|
|
align-items: center;
|
|
color: var(--iti-icon-color);
|
|
}
|
|
|
|
.iti__country-check-svg {
|
|
width: var(--iti-clear-icon-size);
|
|
height: var(--iti-clear-icon-size);
|
|
display: block;
|
|
}
|
|
|
|
/* Spacing between country flag and country name. */
|
|
.iti__country-list .iti__flag {
|
|
margin-right: var(--iti-spacer-horizontal);
|
|
flex-shrink: 0; /* stop long country names from shrinking the flag */
|
|
|
|
[dir="rtl"] & {
|
|
margin-right: 0;
|
|
margin-left: var(--iti-spacer-horizontal);
|
|
}
|
|
}
|
|
|
|
.iti--has-country-selector {
|
|
/* If the adjacent input is disabled/readonly, ensure the country button doesn't look clickable. */
|
|
.iti__country-container:has(+ input[disabled]),
|
|
.iti__country-container:has(+ input[readonly]) {
|
|
button.iti__selected-country {
|
|
cursor: not-allowed;
|
|
|
|
.iti__arrow {
|
|
visibility: hidden;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Show the active background colour on the selected flag/arrow when you hover it, or for as long as the country selector (dropdown) is open. Deliberately not applied when hovering the selected dial code, which would feel weird.
|
|
Only apply when the input is NOT disabled/readonly. */
|
|
.iti__country-container:not(:has(+ input[disabled])):not(:has(+ input[readonly])) {
|
|
.iti__selected-country-primary:hover,
|
|
.iti__selected-country[aria-expanded="true"] .iti__selected-country-primary {
|
|
background-color: var(--iti-hover-color);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* When the country selector is rendered detached (fullscreen, or dropdown with dropdownParent set), increase z-index to prevent display issues. */
|
|
.iti--detached-country-selector {
|
|
position: fixed;
|
|
top: -1000px;
|
|
left: -1000px;
|
|
/* Higher than default Bootstrap modal z-index of 1050. */
|
|
z-index: 1060;
|
|
|
|
&:hover {
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
/* CSS Anchor Positioning: when supported, the browser keeps the detached country selector
|
|
anchored to the input on the compositor (no JS scroll/resize listeners needed) and flips it
|
|
above the input automatically via @position-try when there isn't room below. In unsupporting
|
|
browsers anchor() is invalid so these declarations are dropped, and ui.ts falls back to
|
|
JS-driven positioning (which sets inline top/left, overriding the offscreen defaults above). */
|
|
.iti--detached-country-selector {
|
|
top: calc(anchor(bottom) + 3px);
|
|
left: anchor(left);
|
|
position-try-fallbacks: --iti-flip-above;
|
|
}
|
|
|
|
@position-try --iti-flip-above {
|
|
top: auto;
|
|
bottom: calc(anchor(top) + 3px);
|
|
}
|
|
|
|
/* Put these at the end, so they override other classes with display:flex etc. */
|
|
.iti__hide {
|
|
display: none;
|
|
}
|
|
|
|
/* Need this during init, to get the height of the country list. */
|
|
.iti__v-hide {
|
|
visibility: hidden;
|
|
}
|
|
|
|
/* Overrides for mobile popup. */
|
|
.iti--fullscreen-popup {
|
|
&.iti--detached-country-selector {
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
position: fixed;
|
|
padding: var(--iti-mobile-popup-margin);
|
|
/* Short country lists should be vertically centred. */
|
|
display: flex;
|
|
flex-direction: column;
|
|
/* The country search input auto-focuses, so mobile keyboard appears, so stick to top (also because when filter countries down, the height changes and the vertical centring would make it jump around). */
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.iti__country-selector {
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-height: 100%;
|
|
position: relative; /* Override needed in order to get full-width working properly. */
|
|
}
|
|
|
|
/* give the search input the same v-padding as the country items, for visual consistency */
|
|
.iti__country,
|
|
.iti__search-input {
|
|
padding-top: 10px;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.iti__country {
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
/* Increase line height because country list copy is v likely to overflow on mobile and when it does it needs to be well spaced. */
|
|
line-height: 1.5em;
|
|
}
|
|
}
|
|
|
|
.iti__flag {
|
|
height: var(--iti-flag-height);
|
|
width: var(--iti-flag-width);
|
|
border-radius: 1px;
|
|
box-shadow: 0px 0px 1px 0px #888;
|
|
background-image: image-set(
|
|
var(--iti-path-flags-1x) 1x,
|
|
var(--iti-path-flags-2x) 2x
|
|
);
|
|
background-repeat: no-repeat;
|
|
/* Offset and sprite size are expressed as multiples of --iti-flag-width / --iti-flag-height
|
|
so consumers can scale the flags by overriding just those two vars.
|
|
The 100 fallback hides all flags when no country class is set (useful for the empty state
|
|
shown while initialCountryLookup is resolving) — done via var()'s fallback rather than as a rule
|
|
default so the country-specific offsets win regardless of source order. */
|
|
background-position: calc(var(--iti-flag-offset, 100) * var(--iti-flag-width)) 0;
|
|
background-size: calc(var(--iti-flag-count) * var(--iti-flag-width))
|
|
var(--iti-flag-height);
|
|
}
|
|
|
|
/* Loading state (used while initialCountryLookup is resolving the initial country). */
|
|
.iti__loading {
|
|
position: relative;
|
|
background: none;
|
|
box-shadow: none;
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
margin: auto;
|
|
width: var(--iti-flag-height);
|
|
height: var(--iti-flag-height);
|
|
box-sizing: border-box;
|
|
border: 2px solid var(--iti-icon-color);
|
|
border-right-color: transparent;
|
|
border-radius: 50%;
|
|
animation: iti-spinner 1s linear infinite;
|
|
}
|
|
}
|
|
|
|
@keyframes iti-spinner {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* Subtle visual feedback when strictMode rejects the whole input. Opt-in via the strictRejectAnimation option.
|
|
Targets the input and selected-country button (not the wrapper), so an open dropdown (e.g. with dropdownAlwaysOpen) doesn't shake along with them. */
|
|
.iti__strict-reject-animation {
|
|
.iti__tel-input,
|
|
.iti__selected-country {
|
|
animation: iti-strict-reject-shake 0.3s cubic-bezier(0.36, 0.07, 0.19, 0.97);
|
|
}
|
|
|
|
/* Users who have opted out of motion get a brief background-color flash on the input instead (the selected-country button is skipped to avoid an odd-looking flash on a small UI element). */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.iti__tel-input {
|
|
animation: iti-strict-reject-flash 0.3s ease-out both;
|
|
}
|
|
.iti__selected-country {
|
|
animation: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
@keyframes iti-strict-reject-shake {
|
|
10%,
|
|
90% {
|
|
transform: translateX(-1px);
|
|
}
|
|
20%,
|
|
80% {
|
|
transform: translateX(2px);
|
|
}
|
|
30%,
|
|
50%,
|
|
70% {
|
|
transform: translateX(-3px);
|
|
}
|
|
40%,
|
|
60% {
|
|
transform: translateX(3px);
|
|
}
|
|
}
|
|
|
|
/* Keyframe omits 0%/100% so the element falls back to its base background-color
|
|
(important because `both` fill-mode would otherwise persist the last keyframe). */
|
|
@keyframes iti-strict-reject-flash {
|
|
40% {
|
|
background-color: var(--iti-strict-reject-flash-color);
|
|
}
|
|
}
|
|
|
|
/* Empty state.
|
|
Inline globe icon (now rendered as SVG inside .iti__flag). Keep class for width/position logic and to clear box-shadow. */
|
|
.iti__globe {
|
|
background: none;
|
|
box-shadow: none;
|
|
height: var(--iti-globe-icon-size);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 0; /* ensure svg centers */
|
|
|
|
.iti__globe-svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
fill: var(--iti-icon-color);
|
|
}
|
|
}
|
|
|
|
/* Browsers normally add a coloured outline when you focus an input. Chrome adds a blue outline WITHIN the input. If you focus the input and then hover the selected country, it's bg color square overlaps the focus outline and looks bad. Here, @supports is used to target Chrome only, and move the default outline out by 1px so there's no overlap. */
|
|
@supports (-webkit-appearance: none) and (not (background: -webkit-canvas(foo))) {
|
|
.iti__tel-input:focus {
|
|
outline-offset: 1px;
|
|
}
|
|
}
|