diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss index 2419f9bc96..803babd398 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ActionButton/BitActionButton.scss @@ -26,16 +26,16 @@ } &:focus-visible { - outline: none; + @include focus-ring(var(--bit-acb-clr-hover)); + border-radius: $shp-border-radius; - box-shadow: 0 0 0 spacing(0.25) var(--bit-acb-clr-hover); } &.bit-dis { cursor: default; - color: $clr-fg-dis; pointer-events: none; - --bit-acb-clr-ico: #{$clr-fg-dis}; + color: var(--bit-acb-clr-dis-text); + --bit-acb-clr-ico: var(--bit-acb-clr-dis-text); } } @@ -118,48 +118,56 @@ --bit-acb-clr-ico: #{$clr-pri}; --bit-acb-clr-hover: #{$clr-pri-hover}; --bit-acb-clr-active: #{$clr-pri-active}; + --bit-acb-clr-dis-text: #{$clr-pri-dis-text}; } .bit-acb-sec { --bit-acb-clr-ico: #{$clr-sec}; --bit-acb-clr-hover: #{$clr-sec-hover}; --bit-acb-clr-active: #{$clr-sec-active}; + --bit-acb-clr-dis-text: #{$clr-sec-dis-text}; } .bit-acb-ter { --bit-acb-clr-ico: #{$clr-ter}; --bit-acb-clr-hover: #{$clr-ter-hover}; --bit-acb-clr-active: #{$clr-ter-active}; + --bit-acb-clr-dis-text: #{$clr-ter-dis-text}; } .bit-acb-inf { --bit-acb-clr-ico: #{$clr-inf}; --bit-acb-clr-hover: #{$clr-inf-hover}; --bit-acb-clr-active: #{$clr-inf-active}; + --bit-acb-clr-dis-text: #{$clr-inf-dis-text}; } .bit-acb-suc { --bit-acb-clr-ico: #{$clr-suc}; --bit-acb-clr-hover: #{$clr-suc-hover}; --bit-acb-clr-active: #{$clr-suc-active}; + --bit-acb-clr-dis-text: #{$clr-suc-dis-text}; } .bit-acb-wrn { --bit-acb-clr-ico: #{$clr-wrn}; --bit-acb-clr-hover: #{$clr-wrn-hover}; --bit-acb-clr-active: #{$clr-wrn-active}; + --bit-acb-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-acb-swr { --bit-acb-clr-ico: #{$clr-swr}; --bit-acb-clr-hover: #{$clr-swr-hover}; --bit-acb-clr-active: #{$clr-swr-active}; + --bit-acb-clr-dis-text: #{$clr-swr-dis-text}; } .bit-acb-err { --bit-acb-clr-ico: #{$clr-err}; --bit-acb-clr-hover: #{$clr-err-hover}; --bit-acb-clr-active: #{$clr-err-active}; + --bit-acb-clr-dis-text: #{$clr-err-dis-text}; } @@ -167,54 +175,63 @@ --bit-acb-clr-ico: #{$clr-bg-pri}; --bit-acb-clr-hover: #{$clr-bg-pri-hover}; --bit-acb-clr-active: #{$clr-bg-pri-active}; + --bit-acb-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-acb-sbg { --bit-acb-clr-ico: #{$clr-bg-sec}; --bit-acb-clr-hover: #{$clr-bg-sec-hover}; --bit-acb-clr-active: #{$clr-bg-sec-active}; + --bit-acb-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-acb-tbg { --bit-acb-clr-ico: #{$clr-bg-ter}; --bit-acb-clr-hover: #{$clr-bg-ter-hover}; --bit-acb-clr-active: #{$clr-bg-ter-active}; + --bit-acb-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-acb-pfg { --bit-acb-clr-ico: #{$clr-fg-pri}; --bit-acb-clr-hover: #{$clr-fg-pri-hover}; --bit-acb-clr-active: #{$clr-fg-pri-active}; + --bit-acb-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-acb-sfg { --bit-acb-clr-ico: #{$clr-fg-sec}; --bit-acb-clr-hover: #{$clr-fg-sec-hover}; --bit-acb-clr-active: #{$clr-fg-sec-active}; + --bit-acb-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-acb-tfg { --bit-acb-clr-ico: #{$clr-fg-ter}; --bit-acb-clr-hover: #{$clr-fg-ter-hover}; --bit-acb-clr-active: #{$clr-fg-ter-active}; + --bit-acb-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-acb-pbr { --bit-acb-clr-ico: #{$clr-brd-pri}; --bit-acb-clr-hover: #{$clr-brd-pri-hover}; --bit-acb-clr-active: #{$clr-brd-pri-active}; + --bit-acb-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-acb-sbr { --bit-acb-clr-ico: #{$clr-brd-sec}; --bit-acb-clr-hover: #{$clr-brd-sec-hover}; --bit-acb-clr-active: #{$clr-brd-sec-active}; + --bit-acb-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-acb-tbr { --bit-acb-clr-ico: #{$clr-brd-ter}; --bit-acb-clr-hover: #{$clr-brd-ter-hover}; --bit-acb-clr-active: #{$clr-brd-ter-active}; + --bit-acb-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/Button/BitButton.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/Button/BitButton.scss index 706393d462..d40a0a0b52 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/Button/BitButton.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/Button/BitButton.scss @@ -17,13 +17,14 @@ border-width: $shp-border-width; border-style: $shp-border-style; border-radius: $shp-border-radius; - --bit-btn-clr-bg-dis: #{$clr-bg-dis}; - --bit-btn-clr-brd-dis: #{$clr-brd-dis}; --bit-btn-float-offset: #{spacing(2)}; + &:focus-visible { + @include focus-ring(var(--bit-btn-clr-focus)); + } + &.bit-dis { cursor: default; - color: $clr-fg-dis; pointer-events: none; } @@ -130,8 +131,9 @@ } &.bit-dis { - border-color: var(--bit-btn-clr-brd-dis); - background-color: var(--bit-btn-clr-bg-dis); + color: var(--bit-btn-clr-dis-text); + border-color: var(--bit-btn-clr-dis); + background-color: var(--bit-btn-clr-dis); } } @@ -153,7 +155,9 @@ } &.bit-dis { - border-color: var(--bit-btn-clr-brd-dis); + color: var(--bit-btn-clr-dis-text); + border-color: var(--bit-btn-clr-dis); + background-color: transparent; } } @@ -173,6 +177,12 @@ color: var(--bit-btn-clr-txt); background-color: var(--bit-btn-clr-active); } + + &.bit-dis { + color: var(--bit-btn-clr-dis-text); + border-color: transparent; + background-color: transparent; + } } @@ -181,7 +191,10 @@ --bit-btn-clr-txt: #{$clr-pri-text}; --bit-btn-clr-hover: #{$clr-pri-hover}; --bit-btn-clr-active: #{$clr-pri-active}; + --bit-btn-clr-focus: #{$clr-pri-focus}; --bit-btn-clr-spn: #{$clr-pri-dark}; + --bit-btn-clr-dis: #{$clr-pri-dis}; + --bit-btn-clr-dis-text: #{$clr-pri-dis-text}; } .bit-btn-sec { @@ -189,7 +202,10 @@ --bit-btn-clr-txt: #{$clr-sec-text}; --bit-btn-clr-hover: #{$clr-sec-hover}; --bit-btn-clr-active: #{$clr-sec-active}; + --bit-btn-clr-focus: #{$clr-sec-focus}; --bit-btn-clr-spn: #{$clr-sec-dark}; + --bit-btn-clr-dis: #{$clr-sec-dis}; + --bit-btn-clr-dis-text: #{$clr-sec-dis-text}; } .bit-btn-ter { @@ -197,7 +213,10 @@ --bit-btn-clr-txt: #{$clr-ter-text}; --bit-btn-clr-hover: #{$clr-ter-hover}; --bit-btn-clr-active: #{$clr-ter-active}; + --bit-btn-clr-focus: #{$clr-ter-focus}; --bit-btn-clr-spn: #{$clr-ter-dark}; + --bit-btn-clr-dis: #{$clr-ter-dis}; + --bit-btn-clr-dis-text: #{$clr-ter-dis-text}; } .bit-btn-inf { @@ -205,7 +224,10 @@ --bit-btn-clr-txt: #{$clr-inf-text}; --bit-btn-clr-hover: #{$clr-inf-hover}; --bit-btn-clr-active: #{$clr-inf-active}; + --bit-btn-clr-focus: #{$clr-inf-focus}; --bit-btn-clr-spn: #{$clr-inf-dark}; + --bit-btn-clr-dis: #{$clr-inf-dis}; + --bit-btn-clr-dis-text: #{$clr-inf-dis-text}; } .bit-btn-suc { @@ -213,7 +235,10 @@ --bit-btn-clr-txt: #{$clr-suc-text}; --bit-btn-clr-hover: #{$clr-suc-hover}; --bit-btn-clr-active: #{$clr-suc-active}; + --bit-btn-clr-focus: #{$clr-suc-focus}; --bit-btn-clr-spn: #{$clr-suc-dark}; + --bit-btn-clr-dis: #{$clr-suc-dis}; + --bit-btn-clr-dis-text: #{$clr-suc-dis-text}; } .bit-btn-wrn { @@ -221,7 +246,10 @@ --bit-btn-clr-txt: #{$clr-wrn-text}; --bit-btn-clr-hover: #{$clr-wrn-hover}; --bit-btn-clr-active: #{$clr-wrn-active}; + --bit-btn-clr-focus: #{$clr-wrn-focus}; --bit-btn-clr-spn: #{$clr-wrn-dark}; + --bit-btn-clr-dis: #{$clr-wrn-dis}; + --bit-btn-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-btn-swr { @@ -229,7 +257,10 @@ --bit-btn-clr-txt: #{$clr-swr-text}; --bit-btn-clr-hover: #{$clr-swr-hover}; --bit-btn-clr-active: #{$clr-swr-active}; + --bit-btn-clr-focus: #{$clr-swr-focus}; --bit-btn-clr-spn: #{$clr-swr-dark}; + --bit-btn-clr-dis: #{$clr-swr-dis}; + --bit-btn-clr-dis-text: #{$clr-swr-dis-text}; } .bit-btn-err { @@ -237,7 +268,10 @@ --bit-btn-clr-txt: #{$clr-err-text}; --bit-btn-clr-hover: #{$clr-err-hover}; --bit-btn-clr-active: #{$clr-err-active}; + --bit-btn-clr-focus: #{$clr-err-focus}; --bit-btn-clr-spn: #{$clr-err-dark}; + --bit-btn-clr-dis: #{$clr-err-dis}; + --bit-btn-clr-dis-text: #{$clr-err-dis-text}; } .bit-btn-pbg { @@ -245,7 +279,10 @@ --bit-btn-clr-txt: #{$clr-fg-pri}; --bit-btn-clr-hover: #{$clr-bg-pri-hover}; --bit-btn-clr-active: #{$clr-bg-pri-active}; + --bit-btn-clr-focus: #{$clr-bg-pri-focus}; --bit-btn-clr-spn: #{$clr-fg-pri}; + --bit-btn-clr-dis: #{$clr-bg-pri-dis}; + --bit-btn-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-btn-sbg { @@ -253,7 +290,10 @@ --bit-btn-clr-txt: #{$clr-fg-pri}; --bit-btn-clr-hover: #{$clr-bg-sec-hover}; --bit-btn-clr-active: #{$clr-bg-sec-active}; + --bit-btn-clr-focus: #{$clr-bg-sec}; --bit-btn-clr-spn: #{$clr-fg-pri}; + --bit-btn-clr-dis: #{$clr-bg-sec-dis}; + --bit-btn-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-btn-tbg { @@ -261,7 +301,10 @@ --bit-btn-clr-txt: #{$clr-fg-pri}; --bit-btn-clr-hover: #{$clr-bg-ter-hover}; --bit-btn-clr-active: #{$clr-bg-ter-active}; + --bit-btn-clr-focus: #{$clr-bg-ter}; --bit-btn-clr-spn: #{$clr-fg-pri}; + --bit-btn-clr-dis: #{$clr-bg-ter-dis}; + --bit-btn-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-btn-pfg { @@ -269,7 +312,10 @@ --bit-btn-clr-txt: #{$clr-bg-pri}; --bit-btn-clr-hover: #{$clr-fg-pri-hover}; --bit-btn-clr-active: #{$clr-fg-pri-active}; + --bit-btn-clr-focus: #{$clr-fg-pri-focus}; --bit-btn-clr-spn: #{$clr-bg-pri}; + --bit-btn-clr-dis: #{$clr-fg-pri-dis}; + --bit-btn-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-btn-sfg { @@ -277,7 +323,10 @@ --bit-btn-clr-txt: #{$clr-bg-pri}; --bit-btn-clr-hover: #{$clr-fg-sec-hover}; --bit-btn-clr-active: #{$clr-fg-sec-active}; + --bit-btn-clr-focus: #{$clr-fg-sec}; --bit-btn-clr-spn: #{$clr-bg-pri}; + --bit-btn-clr-dis: #{$clr-fg-sec-dis}; + --bit-btn-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-btn-tfg { @@ -285,7 +334,10 @@ --bit-btn-clr-txt: #{$clr-bg-pri}; --bit-btn-clr-hover: #{$clr-fg-ter-hover}; --bit-btn-clr-active: #{$clr-fg-ter-active}; + --bit-btn-clr-focus: #{$clr-fg-ter}; --bit-btn-clr-spn: #{$clr-bg-pri}; + --bit-btn-clr-dis: #{$clr-fg-ter-dis}; + --bit-btn-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-btn-pbr { @@ -293,7 +345,10 @@ --bit-btn-clr-txt: #{$clr-fg-pri}; --bit-btn-clr-hover: #{$clr-brd-pri-hover}; --bit-btn-clr-active: #{$clr-brd-pri-active}; + --bit-btn-clr-focus: #{$clr-brd-pri-focus}; --bit-btn-clr-spn: #{$clr-fg-pri}; + --bit-btn-clr-dis: #{$clr-brd-pri-dis}; + --bit-btn-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-btn-sbr { @@ -301,7 +356,10 @@ --bit-btn-clr-txt: #{$clr-fg-pri}; --bit-btn-clr-hover: #{$clr-brd-sec-hover}; --bit-btn-clr-active: #{$clr-brd-sec-active}; + --bit-btn-clr-focus: #{$clr-brd-sec}; --bit-btn-clr-spn: #{$clr-fg-pri}; + --bit-btn-clr-dis: #{$clr-brd-sec-dis}; + --bit-btn-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-btn-tbr { @@ -309,7 +367,10 @@ --bit-btn-clr-txt: #{$clr-fg-pri}; --bit-btn-clr-hover: #{$clr-brd-ter-hover}; --bit-btn-clr-active: #{$clr-brd-ter-active}; + --bit-btn-clr-focus: #{$clr-brd-ter}; --bit-btn-clr-spn: #{$clr-fg-pri}; + --bit-btn-clr-dis: #{$clr-brd-ter-dis}; + --bit-btn-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.scss index 3e6fb265db..b5cd25cf57 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ButtonGroup/BitButtonGroup.scss @@ -13,14 +13,17 @@ &.bit-dis, &.bit-dis * { cursor: default; - color: $clr-fg-dis; pointer-events: none; } &.bit-dis { - border-color: $clr-brd-dis; - --bit-btg-itm-clr-bg: #{$clr-bg-dis}; - --bit-btg-itm-clr-brd: #{$clr-brd-dis}; + color: var(--bit-btg-clr-dis-text); + border-color: var(--bit-btg-clr-dis); + + // Resolve through the per-variant `*-dis` tokens so outline/text variants keep transparent + // backgrounds and only filled variants render the colored disabled tint. + --bit-btg-itm-clr-bg: var(--bit-btg-itm-clr-bg-dis); + --bit-btg-itm-clr-brd: var(--bit-btg-itm-clr-brd-dis, var(--bit-btg-clr-dis)); } } @@ -64,8 +67,9 @@ &[disabled] { cursor: default; - color: $clr-fg-dis; pointer-events: none; + color: var(--bit-btg-clr-dis-text); + border-color: var(--bit-btg-itm-clr-brd-dis, var(--bit-btg-clr-dis)); background-color: var(--bit-btg-itm-clr-bg-dis); } @@ -92,7 +96,7 @@ --bit-btg-itm-clr-hover: var(--bit-btg-clr-txt); --bit-btg-itm-clr-bg-hover: var(--bit-btg-clr-hover); --bit-btg-itm-clr-bg-active: var(--bit-btg-clr-active); - --bit-btg-itm-clr-bg-dis: #{$clr-bg-dis}; + --bit-btg-itm-clr-bg-dis: var(--bit-btg-clr-dis); @media (hover: hover) { &:hover { @@ -113,7 +117,7 @@ --bit-btg-itm-clr-hover: var(--bit-btg-clr-txt); --bit-btg-itm-clr-bg-hover: var(--bit-btg-clr-hover); --bit-btg-itm-clr-bg-active: var(--bit-btg-clr-active); - --bit-btg-itm-clr-bg-dis: transprent; + --bit-btg-itm-clr-bg-dis: transparent; @media (hover: hover) { &:hover { @@ -124,6 +128,10 @@ &:active { border-color: var(--bit-btg-clr-active); } + + &.bit-dis { + background-color: transparent; + } } .bit-btg-txt { @@ -134,7 +142,7 @@ --bit-btg-itm-clr-hover: var(--bit-btg-clr-txt); --bit-btg-itm-clr-bg-hover: var(--bit-btg-clr-hover); --bit-btg-itm-clr-bg-active: var(--bit-btg-clr-active); - --bit-btg-itm-clr-bg-dis: transprent; + --bit-btg-itm-clr-bg-dis: transparent; &.bit-dis { border-color: transparent; @@ -168,6 +176,8 @@ --bit-btg-clr-dark: #{$clr-pri-dark}; --bit-btg-clr-dark-hover: #{$clr-pri-dark-hover}; --bit-btg-clr-dark-active: #{$clr-pri-dark-active}; + --bit-btg-clr-dis: #{$clr-pri-dis}; + --bit-btg-clr-dis-text: #{$clr-pri-dis-text}; } .bit-btg-sec { @@ -179,6 +189,8 @@ --bit-btg-clr-dark: #{$clr-sec-dark}; --bit-btg-clr-dark-hover: #{$clr-sec-dark-hover}; --bit-btg-clr-dark-active: #{$clr-sec-dark-active}; + --bit-btg-clr-dis: #{$clr-sec-dis}; + --bit-btg-clr-dis-text: #{$clr-sec-dis-text}; } .bit-btg-ter { @@ -190,6 +202,8 @@ --bit-btg-clr-dark: #{$clr-ter-dark}; --bit-btg-clr-dark-hover: #{$clr-ter-dark-hover}; --bit-btg-clr-dark-active: #{$clr-ter-dark-active}; + --bit-btg-clr-dis: #{$clr-ter-dis}; + --bit-btg-clr-dis-text: #{$clr-ter-dis-text}; } .bit-btg-inf { @@ -201,6 +215,8 @@ --bit-btg-clr-dark: #{$clr-inf-dark}; --bit-btg-clr-dark-hover: #{$clr-inf-dark-hover}; --bit-btg-clr-dark-active: #{$clr-inf-dark-active}; + --bit-btg-clr-dis: #{$clr-inf-dis}; + --bit-btg-clr-dis-text: #{$clr-inf-dis-text}; } .bit-btg-suc { @@ -212,6 +228,8 @@ --bit-btg-clr-dark: #{$clr-suc-dark}; --bit-btg-clr-dark-hover: #{$clr-suc-dark-hover}; --bit-btg-clr-dark-active: #{$clr-suc-dark-active}; + --bit-btg-clr-dis: #{$clr-suc-dis}; + --bit-btg-clr-dis-text: #{$clr-suc-dis-text}; } .bit-btg-wrn { @@ -223,6 +241,8 @@ --bit-btg-clr-dark: #{$clr-wrn-dark}; --bit-btg-clr-dark-hover: #{$clr-wrn-dark-hover}; --bit-btg-clr-dark-active: #{$clr-wrn-dark-active}; + --bit-btg-clr-dis: #{$clr-wrn-dis}; + --bit-btg-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-btg-swr { @@ -234,6 +254,8 @@ --bit-btg-clr-dark: #{$clr-swr-dark}; --bit-btg-clr-dark-hover: #{$clr-swr-dark-hover}; --bit-btg-clr-dark-active: #{$clr-swr-dark-active}; + --bit-btg-clr-dis: #{$clr-swr-dis}; + --bit-btg-clr-dis-text: #{$clr-swr-dis-text}; } .bit-btg-err { @@ -245,6 +267,8 @@ --bit-btg-clr-dark: #{$clr-err-dark}; --bit-btg-clr-dark-hover: #{$clr-err-dark-hover}; --bit-btg-clr-dark-active: #{$clr-err-dark-active}; + --bit-btg-clr-dis: #{$clr-err-dis}; + --bit-btg-clr-dis-text: #{$clr-err-dis-text}; } @@ -257,6 +281,8 @@ --bit-btg-clr-dark: #{$clr-bg-pri}; --bit-btg-clr-dark-hover: #{$clr-bg-pri-hover}; --bit-btg-clr-dark-active: #{$clr-bg-pri-active}; + --bit-btg-clr-dis: #{$clr-bg-pri-dis}; + --bit-btg-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-btg-sbg { @@ -268,6 +294,8 @@ --bit-btg-clr-dark: #{$clr-bg-sec}; --bit-btg-clr-dark-hover: #{$clr-bg-sec-hover}; --bit-btg-clr-dark-active: #{$clr-bg-sec-active}; + --bit-btg-clr-dis: #{$clr-bg-sec-dis}; + --bit-btg-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-btg-tbg { @@ -279,6 +307,8 @@ --bit-btg-clr-dark: #{$clr-bg-ter}; --bit-btg-clr-dark-hover: #{$clr-bg-ter-hover}; --bit-btg-clr-dark-active: #{$clr-bg-ter-active}; + --bit-btg-clr-dis: #{$clr-bg-ter-dis}; + --bit-btg-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-btg-pfg { @@ -290,6 +320,8 @@ --bit-btg-clr-dark: #{$clr-fg-pri}; --bit-btg-clr-dark-hover: #{$clr-fg-pri-hover}; --bit-btg-clr-dark-active: #{$clr-fg-pri-active}; + --bit-btg-clr-dis: #{$clr-fg-pri-dis}; + --bit-btg-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-btg-sfg { @@ -301,6 +333,8 @@ --bit-btg-clr-dark: #{$clr-fg-sec}; --bit-btg-clr-dark-hover: #{$clr-fg-sec-hover}; --bit-btg-clr-dark-active: #{$clr-fg-sec-active}; + --bit-btg-clr-dis: #{$clr-fg-sec-dis}; + --bit-btg-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-btg-tfg { @@ -312,6 +346,8 @@ --bit-btg-clr-dark: #{$clr-fg-ter}; --bit-btg-clr-dark-hover: #{$clr-fg-ter-hover}; --bit-btg-clr-dark-active: #{$clr-fg-ter-active}; + --bit-btg-clr-dis: #{$clr-fg-ter-dis}; + --bit-btg-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-btg-pbr { @@ -323,6 +359,8 @@ --bit-btg-clr-dark: #{$clr-brd-pri}; --bit-btg-clr-dark-hover: #{$clr-brd-pri-hover}; --bit-btg-clr-dark-active: #{$clr-brd-pri-active}; + --bit-btg-clr-dis: #{$clr-brd-pri-dis}; + --bit-btg-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-btg-sbr { @@ -334,6 +372,8 @@ --bit-btg-clr-dark: #{$clr-brd-sec}; --bit-btg-clr-dark-hover: #{$clr-brd-sec-hover}; --bit-btg-clr-dark-active: #{$clr-brd-sec-active}; + --bit-btg-clr-dis: #{$clr-brd-sec-dis}; + --bit-btg-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-btg-tbr { @@ -345,6 +385,8 @@ --bit-btg-clr-dark: #{$clr-brd-ter}; --bit-btg-clr-dark-hover: #{$clr-brd-ter-hover}; --bit-btg-clr-dark-active: #{$clr-brd-ter-active}; + --bit-btg-clr-dis: #{$clr-brd-ter-dis}; + --bit-btg-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.scss index 70180bd280..0ead792d62 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/MenuButton/BitMenuButton.scss @@ -13,11 +13,16 @@ border-style: $shp-border-style; border-radius: $shp-border-radius; + &:focus-visible, + &:focus-within { + @include focus-ring(var(--bit-mnb-clr-focus)); + } + &.bit-dis { cursor: default; - color: $clr-fg-dis; pointer-events: none; - --bit-mnb-clr-spb: #{$clr-fg-dis}; + color: var(--bit-mnb-clr-dis-text); + --bit-mnb-clr-spb: var(--bit-mnb-clr-dis-text); } } @@ -151,7 +156,7 @@ &[disabled] { cursor: default; - color: $clr-fg-dis; + color: var(--bit-mnb-clr-dis-text); pointer-events: none; } @@ -189,8 +194,9 @@ } &.bit-dis { - border-color: $clr-brd-dis; - background-color: $clr-bg-dis; + color: var(--bit-mnb-clr-dis-text); + border-color: var(--bit-mnb-clr-dis); + background-color: var(--bit-mnb-clr-dis); } } @@ -211,7 +217,9 @@ } &.bit-dis { - border-color: $clr-brd-dis; + color: var(--bit-mnb-clr-dis-text); + border-color: var(--bit-mnb-clr-dis); + background-color: transparent; } } @@ -220,6 +228,12 @@ border-color: transparent; background-color: transparent; --bit-mnb-clr-spb: var(--bit-mnb-clr); + + &.bit-dis { + color: var(--bit-mnb-clr-dis-text); + border-color: transparent; + background-color: transparent; + } } .bit-mnb-pri { @@ -227,6 +241,9 @@ --bit-mnb-clr-txt: #{$clr-pri-text}; --bit-mnb-clr-hover: #{$clr-pri-hover}; --bit-mnb-clr-active: #{$clr-pri-active}; + --bit-mnb-clr-focus: #{$clr-pri-focus}; + --bit-mnb-clr-dis: #{$clr-pri-dis}; + --bit-mnb-clr-dis-text: #{$clr-pri-dis-text}; } .bit-mnb-sec { @@ -234,6 +251,9 @@ --bit-mnb-clr-txt: #{$clr-sec-text}; --bit-mnb-clr-hover: #{$clr-sec-hover}; --bit-mnb-clr-active: #{$clr-sec-active}; + --bit-mnb-clr-focus: #{$clr-sec-focus}; + --bit-mnb-clr-dis: #{$clr-sec-dis}; + --bit-mnb-clr-dis-text: #{$clr-sec-dis-text}; } .bit-mnb-ter { @@ -241,6 +261,9 @@ --bit-mnb-clr-txt: #{$clr-ter-text}; --bit-mnb-clr-hover: #{$clr-ter-hover}; --bit-mnb-clr-active: #{$clr-ter-active}; + --bit-mnb-clr-focus: #{$clr-ter-focus}; + --bit-mnb-clr-dis: #{$clr-ter-dis}; + --bit-mnb-clr-dis-text: #{$clr-ter-dis-text}; } .bit-mnb-inf { @@ -248,6 +271,9 @@ --bit-mnb-clr-txt: #{$clr-inf-text}; --bit-mnb-clr-hover: #{$clr-inf-hover}; --bit-mnb-clr-active: #{$clr-inf-active}; + --bit-mnb-clr-focus: #{$clr-inf-focus}; + --bit-mnb-clr-dis: #{$clr-inf-dis}; + --bit-mnb-clr-dis-text: #{$clr-inf-dis-text}; } .bit-mnb-suc { @@ -255,6 +281,9 @@ --bit-mnb-clr-txt: #{$clr-suc-text}; --bit-mnb-clr-hover: #{$clr-suc-hover}; --bit-mnb-clr-active: #{$clr-suc-active}; + --bit-mnb-clr-focus: #{$clr-suc-focus}; + --bit-mnb-clr-dis: #{$clr-suc-dis}; + --bit-mnb-clr-dis-text: #{$clr-suc-dis-text}; } .bit-mnb-wrn { @@ -262,6 +291,9 @@ --bit-mnb-clr-txt: #{$clr-wrn-text}; --bit-mnb-clr-hover: #{$clr-wrn-hover}; --bit-mnb-clr-active: #{$clr-wrn-active}; + --bit-mnb-clr-focus: #{$clr-wrn-focus}; + --bit-mnb-clr-dis: #{$clr-wrn-dis}; + --bit-mnb-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-mnb-swr { @@ -269,6 +301,9 @@ --bit-mnb-clr-txt: #{$clr-swr-text}; --bit-mnb-clr-hover: #{$clr-swr-hover}; --bit-mnb-clr-active: #{$clr-swr-active}; + --bit-mnb-clr-focus: #{$clr-swr-focus}; + --bit-mnb-clr-dis: #{$clr-swr-dis}; + --bit-mnb-clr-dis-text: #{$clr-swr-dis-text}; } .bit-mnb-err { @@ -276,6 +311,9 @@ --bit-mnb-clr-txt: #{$clr-err-text}; --bit-mnb-clr-hover: #{$clr-err-hover}; --bit-mnb-clr-active: #{$clr-err-active}; + --bit-mnb-clr-focus: #{$clr-err-focus}; + --bit-mnb-clr-dis: #{$clr-err-dis}; + --bit-mnb-clr-dis-text: #{$clr-err-dis-text}; } .bit-mnb-pbg { @@ -283,6 +321,9 @@ --bit-mnb-clr-txt: #{$clr-fg-pri}; --bit-mnb-clr-hover: #{$clr-bg-pri-hover}; --bit-mnb-clr-active: #{$clr-bg-pri-active}; + --bit-mnb-clr-focus: #{$clr-bg-pri-focus}; + --bit-mnb-clr-dis: #{$clr-bg-pri-dis}; + --bit-mnb-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-mnb-sbg { @@ -290,6 +331,9 @@ --bit-mnb-clr-txt: #{$clr-fg-pri}; --bit-mnb-clr-hover: #{$clr-bg-sec-hover}; --bit-mnb-clr-active: #{$clr-bg-sec-active}; + --bit-mnb-clr-focus: #{$clr-bg-sec}; + --bit-mnb-clr-dis: #{$clr-bg-sec-dis}; + --bit-mnb-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-mnb-tbg { @@ -297,6 +341,9 @@ --bit-mnb-clr-txt: #{$clr-fg-pri}; --bit-mnb-clr-hover: #{$clr-bg-ter-hover}; --bit-mnb-clr-active: #{$clr-bg-ter-active}; + --bit-mnb-clr-focus: #{$clr-bg-ter}; + --bit-mnb-clr-dis: #{$clr-bg-ter-dis}; + --bit-mnb-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-mnb-pfg { @@ -304,6 +351,9 @@ --bit-mnb-clr-txt: #{$clr-bg-pri}; --bit-mnb-clr-hover: #{$clr-fg-pri-hover}; --bit-mnb-clr-active: #{$clr-fg-pri-active}; + --bit-mnb-clr-focus: #{$clr-fg-pri-focus}; + --bit-mnb-clr-dis: #{$clr-fg-pri-dis}; + --bit-mnb-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-mnb-sfg { @@ -311,6 +361,9 @@ --bit-mnb-clr-txt: #{$clr-bg-pri}; --bit-mnb-clr-hover: #{$clr-fg-sec-hover}; --bit-mnb-clr-active: #{$clr-fg-sec-active}; + --bit-mnb-clr-focus: #{$clr-fg-sec}; + --bit-mnb-clr-dis: #{$clr-fg-sec-dis}; + --bit-mnb-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-mnb-tfg { @@ -318,6 +371,9 @@ --bit-mnb-clr-txt: #{$clr-bg-pri}; --bit-mnb-clr-hover: #{$clr-fg-ter-hover}; --bit-mnb-clr-active: #{$clr-fg-ter-active}; + --bit-mnb-clr-focus: #{$clr-fg-ter}; + --bit-mnb-clr-dis: #{$clr-fg-ter-dis}; + --bit-mnb-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-mnb-pbr { @@ -325,6 +381,9 @@ --bit-mnb-clr-txt: #{$clr-fg-pri}; --bit-mnb-clr-hover: #{$clr-brd-pri-hover}; --bit-mnb-clr-active: #{$clr-brd-pri-active}; + --bit-mnb-clr-focus: #{$clr-brd-pri-focus}; + --bit-mnb-clr-dis: #{$clr-brd-pri-dis}; + --bit-mnb-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-mnb-sbr { @@ -332,6 +391,9 @@ --bit-mnb-clr-txt: #{$clr-fg-pri}; --bit-mnb-clr-hover: #{$clr-brd-sec-hover}; --bit-mnb-clr-active: #{$clr-brd-sec-active}; + --bit-mnb-clr-focus: #{$clr-brd-sec}; + --bit-mnb-clr-dis: #{$clr-brd-sec-dis}; + --bit-mnb-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-mnb-tbr { @@ -339,6 +401,9 @@ --bit-mnb-clr-txt: #{$clr-fg-pri}; --bit-mnb-clr-hover: #{$clr-brd-ter-hover}; --bit-mnb-clr-active: #{$clr-brd-ter-active}; + --bit-mnb-clr-focus: #{$clr-brd-ter}; + --bit-mnb-clr-dis: #{$clr-brd-ter-dis}; + --bit-mnb-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ToggleButton/BitToggleButton.scss b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ToggleButton/BitToggleButton.scss index 11d9f8877a..1af8095aea 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ToggleButton/BitToggleButton.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Buttons/ToggleButton/BitToggleButton.scss @@ -17,9 +17,12 @@ font-size: var(--bit-tgb-fontsize); --bit-tgb-icn-margin: #{spacing(0.5)}; + &:focus-visible { + @include focus-ring(var(--bit-tgb-clr-focus)); + } + &.bit-dis { cursor: default; - color: $clr-fg-dis; pointer-events: none; } } @@ -52,8 +55,9 @@ } &.bit-dis { - border-color: $clr-brd-dis; - background-color: $clr-bg-dis; + color: var(--bit-tgb-clr-dis-text); + border-color: var(--bit-tgb-clr-dis); + background-color: var(--bit-tgb-clr-dis); } } @@ -77,7 +81,9 @@ } &.bit-dis { - border-color: $clr-brd-dis; + color: var(--bit-tgb-clr-dis-text); + border-color: var(--bit-tgb-clr-dis); + background-color: transparent; } } @@ -99,6 +105,12 @@ border-color: var(--bit-tgb-clr-active); background-color: var(--bit-tgb-clr-active); } + + &.bit-dis { + color: var(--bit-tgb-clr-dis-text); + border-color: transparent; + background-color: transparent; + } } .bit-tgb-chk { @@ -124,9 +136,12 @@ --bit-tgb-clr: #{$clr-pri}; --bit-tgb-clr-hover: #{$clr-pri-hover}; --bit-tgb-clr-active: #{$clr-pri-active}; + --bit-tgb-clr-focus: #{$clr-pri-focus}; --bit-tgb-clr-dark: #{$clr-pri-dark}; --bit-tgb-clr-dark-hover: #{$clr-pri-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-pri-dark-active}; + --bit-tgb-clr-dis: #{$clr-pri-dis}; + --bit-tgb-clr-dis-text: #{$clr-pri-dis-text}; } .bit-tgb-sec { @@ -134,9 +149,12 @@ --bit-tgb-clr: #{$clr-sec}; --bit-tgb-clr-hover: #{$clr-sec-hover}; --bit-tgb-clr-active: #{$clr-sec-active}; + --bit-tgb-clr-focus: #{$clr-sec-focus}; --bit-tgb-clr-dark: #{$clr-sec-dark}; --bit-tgb-clr-dark-hover: #{$clr-sec-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-sec-dark-active}; + --bit-tgb-clr-dis: #{$clr-sec-dis}; + --bit-tgb-clr-dis-text: #{$clr-sec-dis-text}; } .bit-tgb-ter { @@ -144,9 +162,12 @@ --bit-tgb-clr: #{$clr-ter}; --bit-tgb-clr-hover: #{$clr-ter-hover}; --bit-tgb-clr-active: #{$clr-ter-active}; + --bit-tgb-clr-focus: #{$clr-ter-focus}; --bit-tgb-clr-dark: #{$clr-ter-dark}; --bit-tgb-clr-dark-hover: #{$clr-ter-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-ter-dark-active}; + --bit-tgb-clr-dis: #{$clr-ter-dis}; + --bit-tgb-clr-dis-text: #{$clr-ter-dis-text}; } .bit-tgb-inf { @@ -154,9 +175,12 @@ --bit-tgb-clr: #{$clr-inf}; --bit-tgb-clr-hover: #{$clr-inf-hover}; --bit-tgb-clr-active: #{$clr-inf-active}; + --bit-tgb-clr-focus: #{$clr-inf-focus}; --bit-tgb-clr-dark: #{$clr-inf-dark}; --bit-tgb-clr-dark-hover: #{$clr-inf-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-inf-dark-active}; + --bit-tgb-clr-dis: #{$clr-inf-dis}; + --bit-tgb-clr-dis-text: #{$clr-inf-dis-text}; } .bit-tgb-suc { @@ -164,9 +188,12 @@ --bit-tgb-clr: #{$clr-suc}; --bit-tgb-clr-hover: #{$clr-suc-hover}; --bit-tgb-clr-active: #{$clr-suc-active}; + --bit-tgb-clr-focus: #{$clr-suc-focus}; --bit-tgb-clr-dark: #{$clr-suc-dark}; --bit-tgb-clr-dark-hover: #{$clr-suc-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-suc-dark-active}; + --bit-tgb-clr-dis: #{$clr-suc-dis}; + --bit-tgb-clr-dis-text: #{$clr-suc-dis-text}; } .bit-tgb-wrn { @@ -174,9 +201,12 @@ --bit-tgb-clr: #{$clr-wrn}; --bit-tgb-clr-hover: #{$clr-wrn-hover}; --bit-tgb-clr-active: #{$clr-wrn-active}; + --bit-tgb-clr-focus: #{$clr-wrn-focus}; --bit-tgb-clr-dark: #{$clr-wrn-dark}; --bit-tgb-clr-dark-hover: #{$clr-wrn-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-wrn-dark-active}; + --bit-tgb-clr-dis: #{$clr-wrn-dis}; + --bit-tgb-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-tgb-swr { @@ -184,9 +214,12 @@ --bit-tgb-clr: #{$clr-swr}; --bit-tgb-clr-hover: #{$clr-swr-hover}; --bit-tgb-clr-active: #{$clr-swr-active}; + --bit-tgb-clr-focus: #{$clr-swr-focus}; --bit-tgb-clr-dark: #{$clr-swr-dark}; --bit-tgb-clr-dark-hover: #{$clr-swr-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-swr-dark-active}; + --bit-tgb-clr-dis: #{$clr-swr-dis}; + --bit-tgb-clr-dis-text: #{$clr-swr-dis-text}; } .bit-tgb-err { @@ -194,9 +227,12 @@ --bit-tgb-clr: #{$clr-err}; --bit-tgb-clr-hover: #{$clr-err-hover}; --bit-tgb-clr-active: #{$clr-err-active}; + --bit-tgb-clr-focus: #{$clr-err-focus}; --bit-tgb-clr-dark: #{$clr-err-dark}; --bit-tgb-clr-dark-hover: #{$clr-err-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-err-dark-active}; + --bit-tgb-clr-dis: #{$clr-err-dis}; + --bit-tgb-clr-dis-text: #{$clr-err-dis-text}; } .bit-tgb-pbg { @@ -204,9 +240,12 @@ --bit-tgb-clr: #{$clr-bg-pri}; --bit-tgb-clr-hover: #{$clr-bg-pri-hover}; --bit-tgb-clr-active: #{$clr-bg-pri-active}; + --bit-tgb-clr-focus: #{$clr-bg-pri-focus}; --bit-tgb-clr-dark: #{$clr-bg-pri-dark}; --bit-tgb-clr-dark-hover: #{$clr-bg-pri-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-bg-pri-dark-active}; + --bit-tgb-clr-dis: #{$clr-bg-pri-dis}; + --bit-tgb-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-tgb-sbg { @@ -214,9 +253,12 @@ --bit-tgb-clr: #{$clr-bg-sec}; --bit-tgb-clr-hover: #{$clr-bg-sec-hover}; --bit-tgb-clr-active: #{$clr-bg-sec-active}; + --bit-tgb-clr-focus: #{$clr-bg-sec}; --bit-tgb-clr-dark: #{$clr-bg-sec-dark}; --bit-tgb-clr-dark-hover: #{$clr-bg-sec-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-bg-sec-dark-active}; + --bit-tgb-clr-dis: #{$clr-bg-sec-dis}; + --bit-tgb-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-tgb-tbg { @@ -224,9 +266,12 @@ --bit-tgb-clr: #{$clr-bg-ter}; --bit-tgb-clr-hover: #{$clr-bg-ter-hover}; --bit-tgb-clr-active: #{$clr-bg-ter-active}; + --bit-tgb-clr-focus: #{$clr-bg-ter}; --bit-tgb-clr-dark: #{$clr-bg-ter-dark}; --bit-tgb-clr-dark-hover: #{$clr-bg-ter-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-bg-ter-dark-active}; + --bit-tgb-clr-dis: #{$clr-bg-ter-dis}; + --bit-tgb-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-tgb-pfg { @@ -234,9 +279,12 @@ --bit-tgb-clr: #{$clr-fg-pri}; --bit-tgb-clr-hover: #{$clr-fg-pri-hover}; --bit-tgb-clr-active: #{$clr-fg-pri-active}; + --bit-tgb-clr-focus: #{$clr-fg-pri-focus}; --bit-tgb-clr-dark: #{$clr-fg-pri-dark}; --bit-tgb-clr-dark-hover: #{$clr-fg-pri-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-fg-pri-dark-active}; + --bit-tgb-clr-dis: #{$clr-fg-pri-dis}; + --bit-tgb-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-tgb-sfg { @@ -244,9 +292,12 @@ --bit-tgb-clr: #{$clr-fg-sec}; --bit-tgb-clr-hover: #{$clr-fg-sec-hover}; --bit-tgb-clr-active: #{$clr-fg-sec-active}; + --bit-tgb-clr-focus: #{$clr-fg-sec}; --bit-tgb-clr-dark: #{$clr-fg-sec-dark}; --bit-tgb-clr-dark-hover: #{$clr-fg-sec-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-fg-sec-dark-active}; + --bit-tgb-clr-dis: #{$clr-fg-sec-dis}; + --bit-tgb-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-tgb-tfg { @@ -254,9 +305,12 @@ --bit-tgb-clr: #{$clr-fg-ter}; --bit-tgb-clr-hover: #{$clr-fg-ter-hover}; --bit-tgb-clr-active: #{$clr-fg-ter-active}; + --bit-tgb-clr-focus: #{$clr-fg-ter}; --bit-tgb-clr-dark: #{$clr-fg-ter-dark}; --bit-tgb-clr-dark-hover: #{$clr-fg-ter-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-fg-ter-dark-active}; + --bit-tgb-clr-dis: #{$clr-fg-ter-dis}; + --bit-tgb-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-tgb-pbr { @@ -264,9 +318,12 @@ --bit-tgb-clr: #{$clr-brd-pri}; --bit-tgb-clr-hover: #{$clr-brd-pri-hover}; --bit-tgb-clr-active: #{$clr-brd-pri-active}; + --bit-tgb-clr-focus: #{$clr-brd-pri-focus}; --bit-tgb-clr-dark: #{$clr-brd-pri-dark}; --bit-tgb-clr-dark-hover: #{$clr-brd-pri-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-brd-pri-dark-active}; + --bit-tgb-clr-dis: #{$clr-brd-pri-dis}; + --bit-tgb-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-tgb-sbr { @@ -274,9 +331,12 @@ --bit-tgb-clr: #{$clr-brd-sec}; --bit-tgb-clr-hover: #{$clr-brd-sec-hover}; --bit-tgb-clr-active: #{$clr-brd-sec-active}; + --bit-tgb-clr-focus: #{$clr-brd-sec}; --bit-tgb-clr-dark: #{$clr-brd-sec-dark}; --bit-tgb-clr-dark-hover: #{$clr-brd-sec-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-brd-sec-dark-active}; + --bit-tgb-clr-dis: #{$clr-brd-sec-dis}; + --bit-tgb-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-tgb-tbr { @@ -284,9 +344,12 @@ --bit-tgb-clr: #{$clr-brd-ter}; --bit-tgb-clr-hover: #{$clr-brd-ter-hover}; --bit-tgb-clr-active: #{$clr-brd-ter-active}; + --bit-tgb-clr-focus: #{$clr-brd-ter}; --bit-tgb-clr-dark: #{$clr-brd-ter-dark}; --bit-tgb-clr-dark-hover: #{$clr-brd-ter-dark-hover}; --bit-tgb-clr-dark-active: #{$clr-brd-ter-dark-active}; + --bit-tgb-clr-dis: #{$clr-brd-ter-dis}; + --bit-tgb-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss index 2e4d4e4486..dc19e6675a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Calendar/BitCalendar.scss @@ -471,9 +471,8 @@ } &:focus { + @include focus-underline-ring; border: none; - outline: none; - border-bottom: $shp-border-width $shp-border-style $clr-brd-pri-active; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Checkbox/BitCheckbox.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Checkbox/BitCheckbox.scss index ca17030c29..348e43227d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Checkbox/BitCheckbox.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Checkbox/BitCheckbox.scss @@ -3,10 +3,15 @@ .bit-chb { cursor: pointer; width: fit-content; + border-radius: $shp-border-radius; font-family: $tg-font-family; --bit-chb-ico-opa: 0; --bit-chb-box-clr-brd: #{$clr-brd-pri}; - --bit-chb-ico-clr: #{var(--bit-chb-clr-txt-sec)}; + --bit-chb-ico-clr: var(--bit-chb-clr-txt-sec); + + &:focus-visible { + @include focus-ring; + } @media (hover: hover) { &:hover { @@ -32,20 +37,20 @@ &.bit-dis { cursor: default; - color: $clr-fg-dis; + color: var(--bit-chb-clr-dis-text); pointer-events: none; - --bit-chb-ico-clr: #{$clr-fg-dis}; - --bit-chb-ico-clr-bg: #{$clr-bg-dis}; - --bit-chb-box-clr-brd: #{$clr-brd-dis}; + --bit-chb-ico-clr: var(--bit-chb-clr-dis-text); + --bit-chb-ico-clr-bg: var(--bit-chb-clr-dis); + --bit-chb-box-clr-brd: var(--bit-chb-clr-dis); &.bit-chb-ckd { - --bit-chb-ico-clr: #{$clr-fg-dis}; - --bit-chb-box-clr-bg: #{$clr-bg-dis}; - --bit-chb-box-clr-brd: #{$clr-bg-dis}; + --bit-chb-ico-clr: var(--bit-chb-clr-dis-text); + --bit-chb-box-clr-bg: var(--bit-chb-clr-dis); + --bit-chb-box-clr-brd: var(--bit-chb-clr-dis); } &.bit-chb-ind { - --bit-chb-ico-clr-bg: #{$clr-bg-dis}; + --bit-chb-ico-clr-bg: var(--bit-chb-clr-dis); } } } @@ -109,35 +114,35 @@ .bit-chb-ckd { - --bit-chb-box-clr-bg: #{var(--bit-chb-clr)}; - --bit-chb-box-clr-brd: #{var(--bit-chb-clr)}; + --bit-chb-box-clr-bg: var(--bit-chb-clr); + --bit-chb-box-clr-brd: var(--bit-chb-clr); --bit-chb-ico-opa: 1; - --bit-chb-ico-clr: #{var(--bit-chb-clr-txt)}; + --bit-chb-ico-clr: var(--bit-chb-clr-txt); @media (hover: hover) { &:hover { - --bit-chb-box-clr-bg: #{var(--bit-chb-clr-hover)}; - --bit-chb-box-clr-brd: #{var(--bit-chb-clr-hover)}; + --bit-chb-box-clr-bg: var(--bit-chb-clr-hover); + --bit-chb-box-clr-brd: var(--bit-chb-clr-hover); --bit-chb-ico-opa: 1; - --bit-chb-ico-clr: #{var(--bit-chb-clr-txt-hover)}; + --bit-chb-ico-clr: var(--bit-chb-clr-txt-hover); } } } .bit-chb-ind { --bit-chb-box-clr-bg: transparent; - --bit-chb-box-clr-brd: #{var(--bit-chb-clr)}; + --bit-chb-box-clr-brd: var(--bit-chb-clr); --bit-chb-ico-opa: 1; - --bit-chb-ico-clr: #{var(--bit-chb-clr-txt)}; - --bit-chb-ico-clr-bg: #{var(--bit-chb-clr)}; + --bit-chb-ico-clr: var(--bit-chb-clr-txt); + --bit-chb-ico-clr-bg: var(--bit-chb-clr); @media (hover: hover) { &:hover { --bit-chb-box-clr-bg: transparent; - --bit-chb-box-clr-brd: #{var(--bit-chb-clr-hover)}; + --bit-chb-box-clr-brd: var(--bit-chb-clr-hover); --bit-chb-ico-opa: 1; - --bit-chb-ico-clr: #{var(--bit-chb-clr-txt-hover)}; - --bit-chb-ico-clr-bg: #{var(--bit-chb-clr-hover)}; + --bit-chb-ico-clr: var(--bit-chb-clr-txt-hover); + --bit-chb-ico-clr-bg: var(--bit-chb-clr-hover); } } @@ -163,6 +168,8 @@ --bit-chb-clr-txt: #{$clr-pri-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-pri-text}; + --bit-chb-clr-dis: #{$clr-pri-dis}; + --bit-chb-clr-dis-text: #{$clr-pri-dis-text}; } .bit-chb-sec { @@ -171,6 +178,8 @@ --bit-chb-clr-txt: #{$clr-sec-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-sec-text}; + --bit-chb-clr-dis: #{$clr-sec-dis}; + --bit-chb-clr-dis-text: #{$clr-sec-dis-text}; } .bit-chb-ter { @@ -179,6 +188,8 @@ --bit-chb-clr-txt: #{$clr-ter-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-ter-text}; + --bit-chb-clr-dis: #{$clr-ter-dis}; + --bit-chb-clr-dis-text: #{$clr-ter-dis-text}; } .bit-chb-inf { @@ -187,6 +198,8 @@ --bit-chb-clr-txt: #{$clr-inf-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-inf-text}; + --bit-chb-clr-dis: #{$clr-inf-dis}; + --bit-chb-clr-dis-text: #{$clr-inf-dis-text}; } .bit-chb-suc { @@ -195,6 +208,8 @@ --bit-chb-clr-txt: #{$clr-suc-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-suc-text}; + --bit-chb-clr-dis: #{$clr-suc-dis}; + --bit-chb-clr-dis-text: #{$clr-suc-dis-text}; } .bit-chb-wrn { @@ -203,6 +218,8 @@ --bit-chb-clr-txt: #{$clr-wrn-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-wrn-text}; + --bit-chb-clr-dis: #{$clr-wrn-dis}; + --bit-chb-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-chb-swr { @@ -211,6 +228,8 @@ --bit-chb-clr-txt: #{$clr-swr-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-swr-text}; + --bit-chb-clr-dis: #{$clr-swr-dis}; + --bit-chb-clr-dis-text: #{$clr-swr-dis-text}; } .bit-chb-err { @@ -219,6 +238,8 @@ --bit-chb-clr-txt: #{$clr-err-text}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-err-text}; + --bit-chb-clr-dis: #{$clr-err-dis}; + --bit-chb-clr-dis-text: #{$clr-err-dis-text}; } .bit-chb-pbg { @@ -227,6 +248,8 @@ --bit-chb-clr-txt: #{$clr-fg-pri}; --bit-chb-clr-txt-sec: #{$clr-bg-sec}; --bit-chb-clr-txt-hover: #{$clr-fg-pri-hover}; + --bit-chb-clr-dis: #{$clr-bg-pri-dis}; + --bit-chb-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-chb-sbg { @@ -235,6 +258,8 @@ --bit-chb-clr-txt: #{$clr-fg-sec}; --bit-chb-clr-txt-sec: #{$clr-bg-sec}; --bit-chb-clr-txt-hover: #{$clr-fg-sec-hover}; + --bit-chb-clr-dis: #{$clr-bg-sec-dis}; + --bit-chb-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-chb-tbg { @@ -243,6 +268,8 @@ --bit-chb-clr-txt: #{$clr-fg-ter}; --bit-chb-clr-txt-sec: #{$clr-bg-sec}; --bit-chb-clr-txt-hover: #{$clr-fg-ter-hover}; + --bit-chb-clr-dis: #{$clr-bg-ter-dis}; + --bit-chb-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-chb-pfg { @@ -251,6 +278,8 @@ --bit-chb-clr-txt: #{$clr-bg-pri}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-bg-pri-hover}; + --bit-chb-clr-dis: #{$clr-fg-pri-dis}; + --bit-chb-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-chb-sfg { @@ -259,6 +288,8 @@ --bit-chb-clr-txt: #{$clr-bg-sec}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-bg-sec-hover}; + --bit-chb-clr-dis: #{$clr-fg-sec-dis}; + --bit-chb-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-chb-tfg { @@ -267,6 +298,8 @@ --bit-chb-clr-txt: #{$clr-bg-ter}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-bg-ter-hover}; + --bit-chb-clr-dis: #{$clr-fg-ter-dis}; + --bit-chb-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-chb-pbr { @@ -275,6 +308,8 @@ --bit-chb-clr-txt: #{$clr-fg-pri}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-fg-pri-hover}; + --bit-chb-clr-dis: #{$clr-brd-pri-dis}; + --bit-chb-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-chb-sbr { @@ -283,6 +318,8 @@ --bit-chb-clr-txt: #{$clr-fg-sec}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-fg-sec-hover}; + --bit-chb-clr-dis: #{$clr-brd-sec-dis}; + --bit-chb-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-chb-tbr { @@ -291,6 +328,8 @@ --bit-chb-clr-txt: #{$clr-fg-ter}; --bit-chb-clr-txt-sec: #{$clr-fg-sec}; --bit-chb-clr-txt-hover: #{$clr-fg-ter-hover}; + --bit-chb-clr-dis: #{$clr-brd-ter-dis}; + --bit-chb-clr-dis-text: #{$clr-brd-ter-dis-text}; } .bit-chb-sm { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss index e0e852a80e..72e3284b4f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/ChoiceGroup/BitChoiceGroup.scss @@ -154,9 +154,15 @@ justify-content: center; min-height: spacing(2.5); transition: all 0.2s ease; + border-radius: $shp-border-radius; padding-block-start: spacing(2.75); border: $shp-border-width $shp-border-style transparent; + &:focus-visible, + input:focus-visible + & { + @include focus-ring; + } + .bit-chg-rad { position: absolute; top: calc(var(--bit-chg-circle-size) / 4); @@ -309,21 +315,21 @@ .bit-chg-rad { cursor: default; pointer-events: none; - color: $clr-fg-dis; + color: var(--bit-chg-clr-dis-text); &::before { - border-color: $clr-brd-dis; + border-color: var(--bit-chg-clr-dis); } &::after { - background-color: $clr-bg-dis; + background-color: var(--bit-chg-clr-dis); } } .bit-chg-ili { cursor: default; border-color: transparent; - color: $clr-fg-dis; + color: var(--bit-chg-clr-dis-text); @media (hover: hover) { &:hover { @@ -333,11 +339,11 @@ } .bit-chg-icw { - color: $clr-fg-dis; + color: var(--bit-chg-clr-dis-text); } .bit-chg-ico { - color: $clr-fg-dis; + color: var(--bit-chg-clr-dis-text); } } @@ -346,6 +352,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-brd-pri}; --bit-chg-clr-hover: #{$clr-pri-hover}; + --bit-chg-clr-dis: #{$clr-pri-dis}; + --bit-chg-clr-dis-text: #{$clr-pri-dis-text}; } .bit-chg-sec { @@ -353,6 +361,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-brd-sec}; --bit-chg-clr-hover: #{$clr-sec-hover}; + --bit-chg-clr-dis: #{$clr-sec-dis}; + --bit-chg-clr-dis-text: #{$clr-sec-dis-text}; } .bit-chg-ter { @@ -360,6 +370,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-brd-ter}; --bit-chg-clr-hover: #{$clr-ter-hover}; + --bit-chg-clr-dis: #{$clr-ter-dis}; + --bit-chg-clr-dis-text: #{$clr-ter-dis-text}; } .bit-chg-inf { @@ -367,6 +379,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-inf}; --bit-chg-clr-hover: #{$clr-inf-hover}; + --bit-chg-clr-dis: #{$clr-inf-dis}; + --bit-chg-clr-dis-text: #{$clr-inf-dis-text}; } .bit-chg-suc { @@ -374,6 +388,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-suc}; --bit-chg-clr-hover: #{$clr-suc-hover}; + --bit-chg-clr-dis: #{$clr-suc-dis}; + --bit-chg-clr-dis-text: #{$clr-suc-dis-text}; } .bit-chg-wrn { @@ -381,6 +397,8 @@ --bit-chg-clr-brd: #{$clr-wrn}; --bit-chg-clr-hover: #{$clr-wrn-hover}; --bit-chg-clr-bg: #{$clr-bg-sec}; + --bit-chg-clr-dis: #{$clr-wrn-dis}; + --bit-chg-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-chg-swr { @@ -388,6 +406,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-swr}; --bit-chg-clr-hover: #{$clr-swr-hover}; + --bit-chg-clr-dis: #{$clr-swr-dis}; + --bit-chg-clr-dis-text: #{$clr-swr-dis-text}; } .bit-chg-err { @@ -395,6 +415,8 @@ --bit-chg-clr-brd: #{$clr-err}; --bit-chg-clr-hover: #{$clr-err-hover}; --bit-chg-clr-bg: #{$clr-bg-sec}; + --bit-chg-clr-dis: #{$clr-err-dis}; + --bit-chg-clr-dis-text: #{$clr-err-dis-text}; } .bit-chg-pbg { @@ -402,6 +424,8 @@ --bit-chg-clr-bg: #{$clr-fg-pri}; --bit-chg-clr-brd: #{$clr-bg-pri}; --bit-chg-clr-hover: #{$clr-bg-pri-hover}; + --bit-chg-clr-dis: #{$clr-bg-pri-dis}; + --bit-chg-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-chg-sbg { @@ -409,6 +433,8 @@ --bit-chg-clr-bg: #{$clr-fg-pri}; --bit-chg-clr-brd: #{$clr-bg-sec}; --bit-chg-clr-hover: #{$clr-bg-sec-hover}; + --bit-chg-clr-dis: #{$clr-bg-sec-dis}; + --bit-chg-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-chg-tbg { @@ -416,6 +442,8 @@ --bit-chg-clr-brd: #{$clr-bg-ter}; --bit-chg-clr-hover: #{$clr-bg-ter-hover}; --bit-chg-clr-bg: #{$clr-fg-pri}; + --bit-chg-clr-dis: #{$clr-bg-ter-dis}; + --bit-chg-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-chg-pfg { @@ -423,6 +451,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-fg-pri}; --bit-chg-clr-hover: #{$clr-fg-pri-hover}; + --bit-chg-clr-dis: #{$clr-fg-pri-dis}; + --bit-chg-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-chg-sfg { @@ -430,6 +460,8 @@ --bit-chg-clr-brd: #{$clr-fg-sec}; --bit-chg-clr-hover: #{$clr-fg-sec-hover}; --bit-chg-clr-bg: #{$clr-bg-sec}; + --bit-chg-clr-dis: #{$clr-fg-sec-dis}; + --bit-chg-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-chg-tfg { @@ -437,6 +469,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-fg-ter}; --bit-chg-clr-hover: #{$clr-fg-ter-hover}; + --bit-chg-clr-dis: #{$clr-fg-ter-dis}; + --bit-chg-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-chg-pbr { @@ -444,6 +478,8 @@ --bit-chg-clr-brd: #{$clr-brd-pri}; --bit-chg-clr-hover: #{$clr-brd-pri-hover}; --bit-chg-clr-bg: #{$clr-bg-sec}; + --bit-chg-clr-dis: #{$clr-brd-pri-dis}; + --bit-chg-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-chg-sbr { @@ -451,6 +487,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-brd-sec}; --bit-chg-clr-hover: #{$clr-brd-sec-hover}; + --bit-chg-clr-dis: #{$clr-brd-sec-dis}; + --bit-chg-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-chg-tbr { @@ -458,6 +496,8 @@ --bit-chg-clr-bg: #{$clr-bg-sec}; --bit-chg-clr-brd: #{$clr-brd-ter}; --bit-chg-clr-hover: #{$clr-brd-ter-hover}; + --bit-chg-clr-dis: #{$clr-brd-ter-dis}; + --bit-chg-clr-dis-text: #{$clr-brd-ter-dis-text}; } .bit-chg-sm { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.scss index 969a882103..f2f970b3bc 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/CircularTimePicker/BitCircularTimePicker.scss @@ -44,6 +44,14 @@ .bit-ctp-icn { border-color: $clr-err; } + + &.bit-ctp-foc:not(.bit-ctp-und) .bit-ctp-icn { + @include focus-ring($clr-err-focus); + } + + &.bit-ctp-und.bit-ctp-foc .bit-ctp-icn { + @include focus-underline-ring($clr-err-focus); + } } } @@ -121,14 +129,9 @@ } } -.bit-ctp-foc { - .bit-ctp-icn::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style $clr-pri; +.bit-ctp-foc:not(.bit-ctp-und) { + .bit-ctp-icn { + @include focus-ring; } } @@ -152,10 +155,8 @@ } &.bit-ctp-foc { - .bit-ctp-icn::after { - border: none; - border-radius: 0; - border-bottom: spacing(0.25) $shp-border-style $clr-brd-pri; + .bit-ctp-icn { + @include focus-underline-ring; } } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.scss index 6d8fc51efa..e01f54128a 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DatePicker/BitDatePicker.scss @@ -50,6 +50,14 @@ .bit-dtp-icn { border-color: $clr-err; } + + &.bit-dtp-foc:not(.bit-dtp-und) .bit-dtp-icn { + @include focus-ring($clr-err-focus); + } + + &.bit-dtp-und.bit-dtp-foc .bit-dtp-icn { + @include focus-underline-ring($clr-err-focus); + } } &.bit-rtl { @@ -162,14 +170,9 @@ } } -.bit-dtp-foc { - .bit-dtp-icn::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style $clr-pri; +.bit-dtp-foc:not(.bit-dtp-und) { + .bit-dtp-icn { + @include focus-ring; } } @@ -655,9 +658,8 @@ } &:focus { + @include focus-underline-ring; border: none; - border-bottom: $shp-border-width $shp-border-style $clr-brd-pri-active; - outline: none; } &:disabled { @@ -722,10 +724,8 @@ } &.bit-dtp-foc { - .bit-dtp-icn::after { - border: none; - border-radius: 0; - border-bottom: spacing(0.25) $shp-border-style $clr-brd-pri; + .bit-dtp-icn { + @include focus-underline-ring; } } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.scss index 0add6f91e9..6817a4a0c1 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/DateRangePicker/BitDateRangePicker.scss @@ -47,9 +47,24 @@ } &.bit-inv { - .bit-dtrp-icp { + .bit-dtrp-icn { border-color: $clr-err; } + + // In underline mode .bit-dtrp-icn has `border: none`, so the border-color above is + // invisible. Render the invalid underline as a box-shadow on .bit-dtrp-icn (mirroring + // the focus indicator) so it shows when invalid and unfocused. + &.bit-dtrp-und .bit-dtrp-icn { + box-shadow: inset 0 calc(-1 * #{$shp-border-width}) 0 0 #{$clr-err}; + } + + &.bit-dtrp-foc:not(.bit-dtrp-und) .bit-dtrp-icn { + @include focus-ring($clr-err-focus); + } + + &.bit-dtrp-und.bit-dtrp-foc .bit-dtrp-icn { + @include focus-underline-ring($clr-err-focus); + } } &.bit-rtl { @@ -162,14 +177,9 @@ } } -.bit-dtrp-foc { - .bit-dtrp-icn::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style $clr-pri; +.bit-dtrp-foc:not(.bit-dtrp-und) { + .bit-dtrp-icn { + @include focus-ring; } } @@ -688,9 +698,8 @@ } &:focus { + @include focus-underline-ring; border: none; - outline: none; - border-bottom: $shp-border-width $shp-border-style $clr-brd-pri-active; } &:disabled { @@ -766,10 +775,8 @@ } &.bit-dtrp-foc { - .bit-dtrp-icn::after { - border: none; - border-radius: 0; - border-bottom: spacing(0.25) $shp-border-style $clr-brd-pri; + .bit-dtrp-icn { + @include focus-underline-ring; } } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.scss index c1dddd43b9..38829bff0e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.scss @@ -31,9 +31,7 @@ } &:focus { - &::after { - border-color: $clr-err; - } + @include focus-ring($clr-err-focus); } } } @@ -54,12 +52,7 @@ &:focus { color: $clr-fg-dis; border-color: $clr-brd-dis; - - &::after { - width: 0; - height: 0; - border: none; - } + box-shadow: none; // Suppress focus ring while disabled. } } @@ -105,9 +98,15 @@ } &:focus { - &::after { - border: none; - } + // Suppress the global box-shadow focus ring on the no-border variant; the + // underline-ring below still applies for keyboard focus via :focus-visible. + box-shadow: none; + } + + &:focus-visible { + // No-border variant: keep the no-border look but use an inset underline-style + // ring as a keyboard-only, high-contrast focus cue (WCAG 2.4.7 / 2.4.13). + @include focus-underline-ring; } } } @@ -170,14 +169,7 @@ color: $clr-fg-pri; } - &::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style var(--bit-drp-clr); - } + @include focus-ring; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.scss index 19c1e37c05..63aa870255 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/NumberField/BitNumberField.scss @@ -52,10 +52,8 @@ border-color: $clr-err; } - &.bit-nfl-fcs { - .bit-nfl-cnt::after { - border-color: $clr-err; - } + &.bit-nfl-fcs .bit-nfl-cnt { + @include focus-ring($clr-err-focus); } } @@ -259,13 +257,8 @@ } .bit-nfl-fcs { - .bit-nfl-cnt::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style $clr-pri; + .bit-nfl-cnt { + @include focus-ring; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.scss index 120f6352fd..ebbdfdea11 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/OtpInput/BitOtpInput.scss @@ -23,6 +23,10 @@ .bit-otp-inp { outline: none; border-color: $clr-err; + + &:focus-visible { + @include focus-ring($clr-err-focus); + } } } } @@ -65,9 +69,8 @@ -webkit-appearance: none } - &:focus { - outline: none; - border: spacing(0.25) $shp-border-style $clr-pri; + &:focus-visible { + @include focus-ring; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.scss index d218bd57eb..30fcef4e13 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/SearchBox/BitSearchBox.scss @@ -12,42 +12,42 @@ pointer-events: none; .bit-srb-cnt { - color: $clr-fg-dis; - border-color: $clr-brd-dis; - background-color: $clr-bg-dis; + color: var(--bit-srb-clr-dis-text); + border-color: var(--bit-srb-clr-dis); + background-color: var(--bit-srb-clr-dis); } .bit-srb-iwp { i { - color: $clr-fg-dis; + color: var(--bit-srb-clr-dis-text); } } .bit-srb-pre, .bit-srb-suf { - color: $clr-fg-dis; + color: var(--bit-srb-clr-dis-text); } @media (hover: hover) { &:hover { .bit-srb-cnt { - border-color: $clr-brd-dis; + border-color: var(--bit-srb-clr-dis); } .bit-srb-iwp { i { - color: $clr-fg-dis; + color: var(--bit-srb-clr-dis-text); } } } } .bit-srb-sbn { - border-color: $clr-brd-dis; - background-color: $clr-bg-dis; + border-color: var(--bit-srb-clr-dis); + background-color: var(--bit-srb-clr-dis); i { - color: $clr-fg-dis; + color: var(--bit-srb-clr-dis-text); } } @@ -84,13 +84,14 @@ } &.bit-srb-foc { - .bit-srb-cnt::after { - border-color: $clr-err; + .bit-srb-cnt { + @include focus-ring($clr-err-focus); } &.bit-srb-und { - .bit-srb-cnt::after { - border-color: $clr-err; + .bit-srb-cnt, + .bit-srb-sbn { + @include focus-underline-ring($clr-err-focus); } } } @@ -111,7 +112,7 @@ &.bit-srb-foc { .bit-srb-sbn { - border-color: var(--bit-srb-clr); + border-color: var(--bit-srb-clr-focus); } } @@ -343,35 +344,27 @@ } .bit-srb-sbn { - border-color: var(--bit-srb-clr); + border-color: var(--bit-srb-clr-focus); } - .bit-srb-cnt::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style var(--bit-srb-clr); + .bit-srb-cnt { + @include focus-ring(var(--bit-srb-clr-focus)); } &.bit-srb-und { - .bit-srb-cnt::after, - .bit-srb-sbn::after { - content: ""; - border: none; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-bottom: spacing(0.25) $shp-border-style var(--bit-srb-clr); + .bit-srb-cnt, + .bit-srb-sbn { + @include focus-underline-ring(var(--bit-srb-clr-focus)); } } } .bit-srb-nbr { - .bit-srb-cnt, - .bit-srb-cnt::after { + .bit-srb-cnt { border: none; + + // Keep box-shadow unset here so the focus-ring mixin applied via .bit-srb-foc + // (and any :focus / :focus-visible states) can still render the focus indicator. } .bit-srb-sbn { @@ -554,6 +547,9 @@ --bit-srb-clr-txt: #{$clr-pri-text}; --bit-srb-clr-hover: #{$clr-pri-hover}; --bit-srb-clr-active: #{$clr-pri-active}; + --bit-srb-clr-focus: #{$clr-pri-focus}; + --bit-srb-clr-dis: #{$clr-pri-dis}; + --bit-srb-clr-dis-text: #{$clr-pri-dis-text}; } .bit-srb-sec { @@ -561,6 +557,9 @@ --bit-srb-clr-txt: #{$clr-sec-text}; --bit-srb-clr-hover: #{$clr-sec-hover}; --bit-srb-clr-active: #{$clr-sec-active}; + --bit-srb-clr-focus: #{$clr-sec-focus}; + --bit-srb-clr-dis: #{$clr-sec-dis}; + --bit-srb-clr-dis-text: #{$clr-sec-dis-text}; } .bit-srb-ter { @@ -568,6 +567,9 @@ --bit-srb-clr-txt: #{$clr-ter-text}; --bit-srb-clr-hover: #{$clr-ter-hover}; --bit-srb-clr-active: #{$clr-ter-active}; + --bit-srb-clr-focus: #{$clr-ter-focus}; + --bit-srb-clr-dis: #{$clr-ter-dis}; + --bit-srb-clr-dis-text: #{$clr-ter-dis-text}; } .bit-srb-inf { @@ -575,6 +577,9 @@ --bit-srb-clr-txt: #{$clr-inf-text}; --bit-srb-clr-hover: #{$clr-inf-hover}; --bit-srb-clr-active: #{$clr-inf-active}; + --bit-srb-clr-focus: #{$clr-inf-focus}; + --bit-srb-clr-dis: #{$clr-inf-dis}; + --bit-srb-clr-dis-text: #{$clr-inf-dis-text}; } .bit-srb-suc { @@ -582,6 +587,9 @@ --bit-srb-clr-txt: #{$clr-suc-text}; --bit-srb-clr-hover: #{$clr-suc-hover}; --bit-srb-clr-active: #{$clr-suc-active}; + --bit-srb-clr-focus: #{$clr-suc-focus}; + --bit-srb-clr-dis: #{$clr-suc-dis}; + --bit-srb-clr-dis-text: #{$clr-suc-dis-text}; } .bit-srb-wrn { @@ -589,6 +597,9 @@ --bit-srb-clr-txt: #{$clr-wrn-text}; --bit-srb-clr-hover: #{$clr-wrn-hover}; --bit-srb-clr-active: #{$clr-wrn-active}; + --bit-srb-clr-focus: #{$clr-wrn-focus}; + --bit-srb-clr-dis: #{$clr-wrn-dis}; + --bit-srb-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-srb-swr { @@ -596,6 +607,9 @@ --bit-srb-clr-txt: #{$clr-swr-text}; --bit-srb-clr-hover: #{$clr-swr-hover}; --bit-srb-clr-active: #{$clr-swr-active}; + --bit-srb-clr-focus: #{$clr-swr-focus}; + --bit-srb-clr-dis: #{$clr-swr-dis}; + --bit-srb-clr-dis-text: #{$clr-swr-dis-text}; } .bit-srb-err { @@ -603,6 +617,9 @@ --bit-srb-clr-txt: #{$clr-err-text}; --bit-srb-clr-hover: #{$clr-err-hover}; --bit-srb-clr-active: #{$clr-err-active}; + --bit-srb-clr-focus: #{$clr-err-focus}; + --bit-srb-clr-dis: #{$clr-err-dis}; + --bit-srb-clr-dis-text: #{$clr-err-dis-text}; } .bit-srb-pbg { @@ -610,6 +627,9 @@ --bit-srb-clr-txt: #{$clr-fg-pri}; --bit-srb-clr-hover: #{$clr-bg-pri-hover}; --bit-srb-clr-active: #{$clr-bg-pri-active}; + --bit-srb-clr-focus: #{$clr-bg-pri-focus}; + --bit-srb-clr-dis: #{$clr-bg-pri-dis}; + --bit-srb-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-srb-sbg { @@ -617,6 +637,9 @@ --bit-srb-clr-txt: #{$clr-fg-pri}; --bit-srb-clr-hover: #{$clr-bg-sec-hover}; --bit-srb-clr-active: #{$clr-bg-sec-active}; + --bit-srb-clr-focus: #{$clr-bg-sec}; + --bit-srb-clr-dis: #{$clr-bg-sec-dis}; + --bit-srb-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-srb-tbg { @@ -624,6 +647,9 @@ --bit-srb-clr-txt: #{$clr-fg-pri}; --bit-srb-clr-hover: #{$clr-bg-ter-hover}; --bit-srb-clr-active: #{$clr-bg-ter-active}; + --bit-srb-clr-focus: #{$clr-bg-ter}; + --bit-srb-clr-dis: #{$clr-bg-ter-dis}; + --bit-srb-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-srb-pfg { @@ -631,6 +657,9 @@ --bit-srb-clr-txt: #{$clr-bg-pri}; --bit-srb-clr-hover: #{$clr-fg-pri-hover}; --bit-srb-clr-active: #{$clr-fg-pri-active}; + --bit-srb-clr-focus: #{$clr-fg-pri-focus}; + --bit-srb-clr-dis: #{$clr-fg-pri-dis}; + --bit-srb-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-srb-sfg { @@ -638,6 +667,9 @@ --bit-srb-clr-txt: #{$clr-bg-pri}; --bit-srb-clr-hover: #{$clr-fg-sec-hover}; --bit-srb-clr-active: #{$clr-fg-sec-active}; + --bit-srb-clr-focus: #{$clr-fg-sec}; + --bit-srb-clr-dis: #{$clr-fg-sec-dis}; + --bit-srb-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-srb-tfg { @@ -645,6 +677,9 @@ --bit-srb-clr-txt: #{$clr-bg-pri}; --bit-srb-clr-hover: #{$clr-fg-ter-hover}; --bit-srb-clr-active: #{$clr-fg-ter-active}; + --bit-srb-clr-focus: #{$clr-fg-ter}; + --bit-srb-clr-dis: #{$clr-fg-ter-dis}; + --bit-srb-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-srb-pbr { @@ -652,6 +687,9 @@ --bit-srb-clr-txt: #{$clr-fg-pri}; --bit-srb-clr-hover: #{$clr-brd-pri-hover}; --bit-srb-clr-active: #{$clr-brd-pri-active}; + --bit-srb-clr-focus: #{$clr-brd-pri-focus}; + --bit-srb-clr-dis: #{$clr-brd-pri-dis}; + --bit-srb-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-srb-sbr { @@ -659,6 +697,9 @@ --bit-srb-clr-txt: #{$clr-fg-pri}; --bit-srb-clr-hover: #{$clr-brd-sec-hover}; --bit-srb-clr-active: #{$clr-brd-sec-active}; + --bit-srb-clr-focus: #{$clr-brd-sec}; + --bit-srb-clr-dis: #{$clr-brd-sec-dis}; + --bit-srb-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-srb-tbr { @@ -666,4 +707,7 @@ --bit-srb-clr-txt: #{$clr-fg-pri}; --bit-srb-clr-hover: #{$clr-brd-ter-hover}; --bit-srb-clr-active: #{$clr-brd-ter-active}; + --bit-srb-clr-focus: #{$clr-brd-ter}; + --bit-srb-clr-dis: #{$clr-brd-ter-dis}; + --bit-srb-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.scss index 1e82079532..1e1b8265c6 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Slider/BitSlider.scss @@ -15,11 +15,30 @@ $BgHover: $clr-pri-light; background: none; &:focus { - outline: none; + outline: none; // The visible focus indicator lives on the thumb pseudo-element below. } } - /**/ - /*webkit*/ + /* webkit focus styles */ + input:focus-visible::-webkit-slider-thumb, + input:focus::-webkit-slider-thumb { + border-color: $clr-pri-focus; + + // Reference the global focus-ring shadow token so forced-colors and theme-level + // overrides (see forced-colors.fluent.scss) propagate to the slider thumb. + box-shadow: var(--bit-shd-focus-ring, + 0 0 0 #{$shp-focus-ring-offset} #{$clr-bg-pri-focus}, + 0 0 0 calc(#{$shp-focus-ring-offset} + #{$shp-focus-ring-width}) #{$clr-pri-focus}); + } + + input:focus-visible::-moz-range-thumb, + input:focus::-moz-range-thumb { + border-color: $clr-pri-focus; + box-shadow: var(--bit-shd-focus-ring, + 0 0 0 #{$shp-focus-ring-offset} #{$clr-bg-pri-focus}, + 0 0 0 calc(#{$shp-focus-ring-offset} + #{$shp-focus-ring-width}) #{$clr-pri-focus}); + } + + /* webkit thumb */ input::-webkit-slider-thumb { width: spacing(2); height: spacing(2); @@ -43,8 +62,7 @@ $BgHover: $clr-pri-light; input::-webkit-slider-runnable-track { background: linear-gradient($BgTrack, $BgTrack) 0/var(--sx) 100% no-repeat, $BgDefult; } - /**/ - /*mozilla*/ + /* mozilla */ input::-moz-range-thumb { width: spacing(2); height: spacing(2); @@ -67,8 +85,7 @@ $BgHover: $clr-pri-light; input::-moz-range-track { background: linear-gradient($BgTrack, $BgTrack) 0/var(--sx) 100% no-repeat, $BgDefult; } - /**/ - /*ms*/ + /* ms */ input::-ms-fill-upper { background: transparent; border-color: transparent; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss index 4f9dcf4c00..d7aebfb981 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TagsInput/BitTagsInput.scss @@ -23,6 +23,12 @@ .bit-tgi-cnt { border-color: $clr-err; } + + &.bit-tgi-fcs .bit-tgi-cnt { + @include focus-ring($clr-err-focus); + + border-color: $clr-err-focus; + } } @media (hover: hover) { @@ -42,19 +48,9 @@ .bit-tgi-fcs { .bit-tgi-cnt { - border-color: $clr-pri; - outline: none; - - &::after { - content: ""; - height: 2px; - left: -1px; - right: -1px; - bottom: -1px; - position: absolute; - background-color: $clr-pri; - border-radius: 0 0 $shp-border-radius $shp-border-radius; - } + @include focus-ring; + + border-color: $clr-pri-focus; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss index 0075196c42..8a534fbfd0 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TextField/BitTextField.scss @@ -69,13 +69,17 @@ } &.bit-tfl-fcs { - .bit-tfl-fgp::after { - border-color: $clr-err; + .bit-tfl-fgp { + @include focus-ring($clr-err-focus); } &.bit-tfl-und { - .bit-tfl-wrp::after { - border-color: $clr-err; + .bit-tfl-fgp { + box-shadow: none; + } + + .bit-tfl-wrp { + @include focus-underline-ring($clr-err-focus); } } } @@ -313,26 +317,17 @@ color: var(--bit-tfl-clr); } - .bit-tfl-fgp::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style var(--bit-tfl-clr); + .bit-tfl-fgp { + @include focus-ring(var(--bit-tfl-clr)); } &.bit-tfl-und { - .bit-tfl-fgp::after { - border: none; + .bit-tfl-fgp { + box-shadow: none; // underlined variant uses a bottom rule instead of a full ring. } - .bit-tfl-wrp::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-bottom: spacing(0.25) $shp-border-style var(--bit-tfl-clr); + .bit-tfl-wrp { + @include focus-underline-ring(var(--bit-tfl-clr)); } } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.scss index 881531c550..49eb53243e 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/TimePicker/BitTimePicker.scss @@ -41,6 +41,14 @@ .bit-tpc-icn { border-color: $clr-err; } + + &.bit-tpc-foc .bit-tpc-icn { + @include focus-ring($clr-err-focus); + } + + &.bit-tpc-und.bit-tpc-foc .bit-tpc-icn { + @include focus-underline-ring($clr-err-focus); + } } &.bit-rtl { @@ -129,13 +137,8 @@ } .bit-tpc-foc { - .bit-tpc-icn::after { - content: ""; - position: absolute; - pointer-events: none; - inset: spacing(-0.125); - border-radius: $shp-border-radius; - border: spacing(0.25) $shp-border-style $clr-pri; + .bit-tpc-icn { + @include focus-ring; } } @@ -159,10 +162,8 @@ } &.bit-tpc-foc { - .bit-tpc-icn::after { - border: none; - border-radius: 0; - border-bottom: spacing(0.25) $shp-border-style $clr-brd-pri; + .bit-tpc-icn { + @include focus-underline-ring; } } } @@ -281,9 +282,8 @@ } &:focus { + @include focus-underline-ring; border: none; - border-bottom: $shp-border-width $shp-border-style $clr-brd-pri-active; - outline: none; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss index 272168857b..10b84879e8 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Inputs/Toggle/BitToggle.scss @@ -120,6 +120,10 @@ border-radius: spacing(1.25); border: $shp-border-width $shp-border-style $clr-brd-pri; + &:focus-visible { + @include focus-ring; + } + @media (hover: hover) { &:hover { border-color: $clr-brd-pri-hover; diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Grid/BitGrid.scss b/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Grid/BitGrid.scss index 319e63626e..a2f1b02651 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Grid/BitGrid.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Layouts/Grid/BitGrid.scss @@ -1,4 +1,4 @@ -@import "../../../Styles/functions.scss"; +@import "../../../Styles/functions.scss"; @import "../../../Styles/media-queries.scss"; .bit-grd { @@ -13,7 +13,7 @@ .bit-grd-itm { flex-basis: calc((100% / var(--columns) * var(--span)) - var(--spacing)); - $breakpoints: ( 'xs': $brk-sm-min, 'sm': $brk-md-min, 'md': $brk-lg-min, 'lg': $brk-xl-min, 'xl': $brk-xxl-min ); + $breakpoints: ( 'xs': $mq-sm-min, 'sm': $mq-md-min, 'md': $mq-lg-min, 'lg': $mq-xl-min, 'xl': $mq-xxl-min ); @each $name, $breakpoint in $breakpoints { @media (min-width: $breakpoint) { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.scss b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.scss index 26d1728e43..67f42cd0f9 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Lists/Timeline/BitTimeline.scss @@ -17,9 +17,9 @@ --bit-tln-dvd-top: 0; --bit-tln-dvd-height: 100%; --bit-tln-dvd-width: #{spacing(0.25)}; - --bit-tln-clr-dis: #{$clr-fg-dis}; - --bit-tln-clr-bg-dis: #{$clr-bg-dis}; - --bit-tln-clr-brd-dis: #{$clr-brd-dis}; + --bit-tln-clr-dis: var(--bit-tln-clr-dis-text); + --bit-tln-clr-bg-dis: var(--bit-tln-clr-dis-bg); + --bit-tln-clr-brd-dis: var(--bit-tln-clr-dis-bg); &.bit-dis { cursor: default; @@ -49,7 +49,7 @@ user-select: none; pointer-events: none; color: var(--bit-tln-clr-dis); - --bit-tln-dot-ico-clr: #{$clr-fg-dis}; + --bit-tln-dot-ico-clr: var(--bit-tln-clr-dis-text); } } @@ -181,48 +181,64 @@ --bit-tln-clr: #{$clr-pri}; --bit-tln-clr-fg: #{$clr-pri-text}; --bit-tln-clr-ico: #{$clr-pri-text}; + --bit-tln-clr-dis-bg: #{$clr-pri-dis}; + --bit-tln-clr-dis-text: #{$clr-pri-dis-text}; } .bit-tln-sec, .bit-tln-ise { --bit-tln-clr: #{$clr-sec}; --bit-tln-clr-fg: #{$clr-sec-text}; --bit-tln-clr-ico: #{$clr-sec-text}; + --bit-tln-clr-dis-bg: #{$clr-sec-dis}; + --bit-tln-clr-dis-text: #{$clr-sec-dis-text}; } .bit-tln-ter, .bit-tln-ite { --bit-tln-clr: #{$clr-ter}; --bit-tln-clr-fg: #{$clr-ter-text}; --bit-tln-clr-ico: #{$clr-ter-text}; + --bit-tln-clr-dis-bg: #{$clr-ter-dis}; + --bit-tln-clr-dis-text: #{$clr-ter-dis-text}; } .bit-tln-inf, .bit-tln-iin { --bit-tln-clr: #{$clr-inf}; --bit-tln-clr-fg: #{$clr-inf-text}; --bit-tln-clr-ico: #{$clr-inf-text}; + --bit-tln-clr-dis-bg: #{$clr-inf-dis}; + --bit-tln-clr-dis-text: #{$clr-inf-dis-text}; } .bit-tln-suc, .bit-tln-isu { --bit-tln-clr: #{$clr-suc}; --bit-tln-clr-fg: #{$clr-suc-text}; --bit-tln-clr-ico: #{$clr-suc-text}; + --bit-tln-clr-dis-bg: #{$clr-suc-dis}; + --bit-tln-clr-dis-text: #{$clr-suc-dis-text}; } .bit-tln-wrn, .bit-tln-iwr { --bit-tln-clr: #{$clr-wrn}; --bit-tln-clr-fg: #{$clr-wrn-text}; --bit-tln-clr-ico: #{$clr-wrn-text}; + --bit-tln-clr-dis-bg: #{$clr-wrn-dis}; + --bit-tln-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-tln-swr, .bit-tln-isw { --bit-tln-clr: #{$clr-swr}; --bit-tln-clr-fg: #{$clr-swr-text}; --bit-tln-clr-ico: #{$clr-swr-text}; + --bit-tln-clr-dis-bg: #{$clr-swr-dis}; + --bit-tln-clr-dis-text: #{$clr-swr-dis-text}; } .bit-tln-err, .bit-tln-ier { --bit-tln-clr: #{$clr-err}; --bit-tln-clr-fg: #{$clr-err-text}; --bit-tln-clr-ico: #{$clr-err-text}; + --bit-tln-clr-dis-bg: #{$clr-err-dis}; + --bit-tln-clr-dis-text: #{$clr-err-dis-text}; } .bit-tln-sm, .bit-tln-ism { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss index 49baafcb2e..8f17d3d8fb 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Breadcrumb/BitBreadcrumb.scss @@ -146,6 +146,11 @@ line-height: spacing(2.5); background-color: transparent; border: $shp-border-width $shp-border-style transparent; + border-radius: $shp-border-radius; + + &:focus-visible { + @include focus-ring; + } } .bit-brc-ofi { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss index 8f8300bfbf..d7b64d2883 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/DropMenu/BitDropMenu.scss @@ -50,6 +50,11 @@ justify-content: center; background-color: transparent; padding: spacing(1.0) spacing(1.5); + border-radius: $shp-border-radius; + + &:focus-visible { + @include focus-ring; + } } .bit-drm-trn { diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.scss index 07576906f2..b1a1954607 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/NavBar/BitNavBar.scss @@ -9,7 +9,7 @@ pointer-events: none; .bit-nbr-itm { - color: $clr-fg-dis; + color: var(--bit-nbr-clr-dis, var(--bit-nbr-clr, currentColor)); } } } @@ -64,75 +64,92 @@ } .bit-nbr-dis { - color: $clr-fg-dis; + color: var(--bit-nbr-clr-dis, var(--bit-nbr-clr, currentColor)); pointer-events: none; } .bit-nbr-pri { --bit-nbr-clr: #{$clr-pri}; + --bit-nbr-clr-dis: #{$clr-pri-dis-text}; } .bit-nbr-sec { --bit-nbr-clr: #{$clr-sec}; + --bit-nbr-clr-dis: #{$clr-sec-dis-text}; } .bit-nbr-ter { --bit-nbr-clr: #{$clr-ter}; + --bit-nbr-clr-dis: #{$clr-ter-dis-text}; } .bit-nbr-inf { --bit-nbr-clr: #{$clr-inf}; + --bit-nbr-clr-dis: #{$clr-inf-dis-text}; } .bit-nbr-suc { --bit-nbr-clr: #{$clr-suc}; + --bit-nbr-clr-dis: #{$clr-suc-dis-text}; } .bit-nbr-wrn { --bit-nbr-clr: #{$clr-wrn}; + --bit-nbr-clr-dis: #{$clr-wrn-dis-text}; } .bit-nbr-swr { --bit-nbr-clr: #{$clr-swr}; + --bit-nbr-clr-dis: #{$clr-swr-dis-text}; } .bit-nbr-err { --bit-nbr-clr: #{$clr-err}; + --bit-nbr-clr-dis: #{$clr-err-dis-text}; } .bit-nbr-pbg { --bit-nbr-clr: #{$clr-bg-pri}; + --bit-nbr-clr-dis: #{$clr-bg-pri-dis-text}; } .bit-nbr-sbg { --bit-nbr-clr: #{$clr-bg-sec}; + --bit-nbr-clr-dis: #{$clr-bg-sec-dis-text}; } .bit-nbr-tbg { --bit-nbr-clr: #{$clr-bg-ter}; + --bit-nbr-clr-dis: #{$clr-bg-ter-dis-text}; } .bit-nbr-pfg { --bit-nbr-clr: #{$clr-fg-pri}; + --bit-nbr-clr-dis: #{$clr-fg-pri-dis-text}; } .bit-nbr-sfg { --bit-nbr-clr: #{$clr-fg-sec}; + --bit-nbr-clr-dis: #{$clr-fg-sec-dis-text}; } .bit-nbr-tfg { --bit-nbr-clr: #{$clr-fg-ter}; + --bit-nbr-clr-dis: #{$clr-fg-ter-dis-text}; } .bit-nbr-pbr { --bit-nbr-clr: #{$clr-brd-pri}; + --bit-nbr-clr-dis: #{$clr-brd-pri-dis-text}; } .bit-nbr-sbr { --bit-nbr-clr: #{$clr-brd-sec}; + --bit-nbr-clr-dis: #{$clr-brd-sec-dis-text}; } .bit-nbr-tbr { --bit-nbr-clr: #{$clr-brd-ter}; + --bit-nbr-clr-dis: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss index be21d2f680..117b5f8c48 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pagination/BitPagination.scss @@ -8,8 +8,8 @@ font-size: var(--bit-pgn-font-size); --bit-pgn-ico-transform-end: scaleX(1); --bit-pgn-ico-transform-start: scaleX(-1); - --bit-pgn-clr-dis: #{$clr-bg-dis}; - --bit-pgn-clr-txt-dis: #{$clr-fg-dis}; + --bit-pgn-clr-dis: var(--bit-pgn-clr-dis-bg); + --bit-pgn-clr-txt-dis: var(--bit-pgn-clr-dis-text); --bit-pgn-clr-btn-txt-hover: var(--bit-pgn-clr-txt); --bit-pgn-clr-btn-bg-hover: var(--bit-pgn-clr-hover); --bit-pgn-clr-btn-brd-hover: var(--bit-pgn-clr-hover); @@ -34,8 +34,8 @@ &.bit-dis { cursor: default; - color: $clr-fg-dis; pointer-events: none; + color: var(--bit-pgn-clr-txt-dis); } } @@ -90,7 +90,6 @@ &[disabled] { cursor: default; - color: $clr-fg-dis; pointer-events: none; color: var(--bit-pgn-clr-btn-txt-dis); border-color: var(--bit-pgn-clr-btn-brd-dis); @@ -151,6 +150,8 @@ --bit-pgn-clr-sel: #{$clr-pri-dark}; --bit-pgn-clr-sel-hover: #{$clr-pri-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-pri-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-pri-dis}; + --bit-pgn-clr-dis-text: #{$clr-pri-dis-text}; } .bit-pgn-sec { @@ -161,6 +162,8 @@ --bit-pgn-clr-sel: #{$clr-sec-dark}; --bit-pgn-clr-sel-hover: #{$clr-sec-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-sec-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-sec-dis}; + --bit-pgn-clr-dis-text: #{$clr-sec-dis-text}; } .bit-pgn-ter { @@ -171,6 +174,8 @@ --bit-pgn-clr-sel: #{$clr-ter-dark}; --bit-pgn-clr-sel-hover: #{$clr-ter-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-ter-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-ter-dis}; + --bit-pgn-clr-dis-text: #{$clr-ter-dis-text}; } .bit-pgn-inf { @@ -181,6 +186,8 @@ --bit-pgn-clr-sel: #{$clr-inf-dark}; --bit-pgn-clr-sel-hover: #{$clr-inf-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-inf-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-inf-dis}; + --bit-pgn-clr-dis-text: #{$clr-inf-dis-text}; } .bit-pgn-suc { @@ -191,6 +198,8 @@ --bit-pgn-clr-sel: #{$clr-suc-dark}; --bit-pgn-clr-sel-hover: #{$clr-suc-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-suc-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-suc-dis}; + --bit-pgn-clr-dis-text: #{$clr-suc-dis-text}; } .bit-pgn-wrn { @@ -201,6 +210,8 @@ --bit-pgn-clr-sel: #{$clr-wrn-dark}; --bit-pgn-clr-sel-hover: #{$clr-wrn-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-wrn-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-wrn-dis}; + --bit-pgn-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-pgn-swr { @@ -211,6 +222,8 @@ --bit-pgn-clr-sel: #{$clr-swr-dark}; --bit-pgn-clr-sel-hover: #{$clr-swr-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-swr-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-swr-dis}; + --bit-pgn-clr-dis-text: #{$clr-swr-dis-text}; } .bit-pgn-err { @@ -221,6 +234,8 @@ --bit-pgn-clr-sel: #{$clr-err-dark}; --bit-pgn-clr-sel-hover: #{$clr-err-dark-hover}; --bit-pgn-clr-sel-active: #{$clr-err-dark-active}; + --bit-pgn-clr-dis-bg: #{$clr-err-dis}; + --bit-pgn-clr-dis-text: #{$clr-err-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss index a74b4e844e..a9ffcdab64 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Navs/Pivot/BitPivot.scss @@ -8,17 +8,17 @@ .content-container { cursor: default; pointer-events: none; - color: $clr-fg-dis; + color: var(--bit-pvt-clr-dis-text); } .bit-pvti { cursor: default; pointer-events: none; - color: $clr-fg-dis; + color: var(--bit-pvt-clr-dis-text); } .bit-pvti-sel::before { - background-color: $clr-brd-dis; + background-color: var(--bit-pvt-clr-dis); } } } @@ -168,6 +168,10 @@ background-color: transparent; color: $clr-fg-pri; + &:focus-visible { + @include focus-ring(var(--bit-pvt-clr)); + } + > span { height: 100%; display: flex; @@ -180,10 +184,10 @@ &.bit-dis { cursor: default; pointer-events: none; - color: $clr-fg-dis; + color: var(--bit-pvt-clr-dis-text); &.bit-pvti-sel::before { - background-color: $clr-brd-dis; + background-color: var(--bit-pvt-clr-dis); } } } @@ -207,6 +211,10 @@ line-height: spacing(5.5); background-color: transparent; + &:focus-visible { + @include focus-ring(var(--bit-pvt-clr)); + } + &::before { bottom: 0; content: ""; @@ -250,48 +258,64 @@ --bit-pvt-clr: #{$clr-pri}; --bit-pvt-clr-hover: #{$clr-pri-hover}; --bit-pvt-clr-text: #{$clr-pri-text}; + --bit-pvt-clr-dis: #{$clr-pri-dis}; + --bit-pvt-clr-dis-text: #{$clr-pri-dis-text}; } .bit-pvt-sec { --bit-pvt-clr: #{$clr-sec}; --bit-pvt-clr-hover: #{$clr-sec-hover}; --bit-pvt-clr-text: #{$clr-sec-text}; + --bit-pvt-clr-dis: #{$clr-sec-dis}; + --bit-pvt-clr-dis-text: #{$clr-sec-dis-text}; } .bit-pvt-ter { --bit-pvt-clr: #{$clr-ter}; --bit-pvt-clr-hover: #{$clr-ter-hover}; --bit-pvt-clr-text: #{$clr-ter-text}; + --bit-pvt-clr-dis: #{$clr-ter-dis}; + --bit-pvt-clr-dis-text: #{$clr-ter-dis-text}; } .bit-pvt-inf { --bit-pvt-clr: #{$clr-inf}; --bit-pvt-clr-hover: #{$clr-inf-hover}; --bit-pvt-clr-text: #{$clr-inf-text}; + --bit-pvt-clr-dis: #{$clr-inf-dis}; + --bit-pvt-clr-dis-text: #{$clr-inf-dis-text}; } .bit-pvt-suc { --bit-pvt-clr: #{$clr-suc}; --bit-pvt-clr-hover: #{$clr-suc-hover}; --bit-pvt-clr-text: #{$clr-suc-text}; + --bit-pvt-clr-dis: #{$clr-suc-dis}; + --bit-pvt-clr-dis-text: #{$clr-suc-dis-text}; } .bit-pvt-wrn { --bit-pvt-clr: #{$clr-wrn}; --bit-pvt-clr-hover: #{$clr-wrn-hover}; --bit-pvt-clr-text: #{$clr-wrn-text}; + --bit-pvt-clr-dis: #{$clr-wrn-dis}; + --bit-pvt-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-pvt-swr { --bit-pvt-clr: #{$clr-swr}; --bit-pvt-clr-hover: #{$clr-swr-hover}; --bit-pvt-clr-text: #{$clr-swr-text}; + --bit-pvt-clr-dis: #{$clr-swr-dis}; + --bit-pvt-clr-dis-text: #{$clr-swr-dis-text}; } .bit-pvt-err { --bit-pvt-clr: #{$clr-err}; --bit-pvt-clr-hover: #{$clr-err-hover}; --bit-pvt-clr-text: #{$clr-err-text}; + --bit-pvt-clr-dis: #{$clr-err-dis}; + --bit-pvt-clr-dis-text: #{$clr-err-dis-text}; } @@ -299,52 +323,70 @@ --bit-pvt-clr: #{$clr-bg-pri}; --bit-pvt-clr-hover: #{$clr-bg-pri-hover}; --bit-pvt-clr-text: #{$clr-fg-pri}; + --bit-pvt-clr-dis: #{$clr-bg-pri-dis}; + --bit-pvt-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-pvt-sbg { --bit-pvt-clr: #{$clr-bg-sec}; --bit-pvt-clr-hover: #{$clr-bg-sec-hover}; --bit-pvt-clr-text: #{$clr-fg-pri}; + --bit-pvt-clr-dis: #{$clr-bg-sec-dis}; + --bit-pvt-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-pvt-tbg { --bit-pvt-clr: #{$clr-bg-ter}; --bit-pvt-clr-hover: #{$clr-bg-ter-hover}; --bit-pvt-clr-text: #{$clr-fg-pri}; + --bit-pvt-clr-dis: #{$clr-bg-ter-dis}; + --bit-pvt-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-pvt-pfg { --bit-pvt-clr: #{$clr-fg-pri}; --bit-pvt-clr-hover: #{$clr-fg-pri-hover}; --bit-pvt-clr-text: #{$clr-bg-pri}; + --bit-pvt-clr-dis: #{$clr-fg-pri-dis}; + --bit-pvt-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-pvt-sfg { --bit-pvt-clr: #{$clr-fg-sec}; --bit-pvt-clr-hover: #{$clr-fg-sec-hover}; --bit-pvt-clr-text: #{$clr-bg-pri}; + --bit-pvt-clr-dis: #{$clr-fg-sec-dis}; + --bit-pvt-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-pvt-tfg { --bit-pvt-clr: #{$clr-fg-ter}; --bit-pvt-clr-hover: #{$clr-fg-ter-hover}; --bit-pvt-clr-text: #{$clr-bg-pri}; + --bit-pvt-clr-dis: #{$clr-fg-ter-dis}; + --bit-pvt-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-pvt-pbr { --bit-pvt-clr: #{$clr-brd-pri}; --bit-pvt-clr-hover: #{$clr-brd-pri-hover}; --bit-pvt-clr-text: #{$clr-fg-pri}; + --bit-pvt-clr-dis: #{$clr-brd-pri-dis}; + --bit-pvt-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-pvt-sbr { --bit-pvt-clr: #{$clr-brd-sec}; --bit-pvt-clr-hover: #{$clr-brd-sec-hover}; --bit-pvt-clr-text: #{$clr-fg-pri}; + --bit-pvt-clr-dis: #{$clr-brd-sec-dis}; + --bit-pvt-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-pvt-tbr { --bit-pvt-clr: #{$clr-brd-ter}; --bit-pvt-clr-hover: #{$clr-brd-ter-hover}; --bit-pvt-clr-text: #{$clr-fg-pri}; + --bit-pvt-clr-dis: #{$clr-brd-ter-dis}; + --bit-pvt-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.scss b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.scss index 29312bc1cc..21de67150c 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Badge/BitBadge.scss @@ -5,7 +5,7 @@ display: inline-block; &.bit-dis { - --bit-bdg-cnt-clr-txt: #{$clr-fg-dis}; + --bit-bdg-cnt-clr-txt: var(--bit-bdg-clr-dis-text); --bit-bdg-cnt-clr-bg: var(--bit-bdg-clr-bg-dis); --bit-bdg-cnt-clr-brd: var(--bit-bdg-clr-brd-dis); } @@ -292,8 +292,8 @@ .bit-bdg-fil { - --bit-bdg-clr-bg-dis: #{$clr-bg-dis}; - --bit-bdg-clr-brd-dis: #{$clr-brd-dis}; + --bit-bdg-clr-bg-dis: var(--bit-bdg-clr-dis); + --bit-bdg-clr-brd-dis: var(--bit-bdg-clr-dis); --bit-bdg-cnt-clr-bg: var(--bit-bdg-clr); --bit-bdg-cnt-clr-brd: var(--bit-bdg-clr); --bit-bdg-cnt-clr-txt: var(--bit-bdg-clr-txt); @@ -301,7 +301,8 @@ .bit-bdg-otl { --bit-bdg-cnt-clr-bg: transparent; - --bit-bdg-clr-brd-dis: #{$clr-brd-dis}; + --bit-bdg-clr-bg-dis: transparent; + --bit-bdg-clr-brd-dis: var(--bit-bdg-clr-dis); --bit-bdg-cnt-clr-txt: var(--bit-bdg-clr); --bit-bdg-cnt-clr-brd: var(--bit-bdg-clr); } @@ -309,6 +310,7 @@ .bit-bdg-txt { --bit-bdg-cnt-clr-bg: transparent; --bit-bdg-cnt-clr-brd: transparent; + --bit-bdg-clr-bg-dis: transparent; --bit-bdg-clr-brd-dis: transparent; --bit-bdg-cnt-clr-txt: var(--bit-bdg-clr); } @@ -317,41 +319,57 @@ .bit-bdg-pri { --bit-bdg-clr: #{$clr-pri}; --bit-bdg-clr-txt: #{$clr-pri-text}; + --bit-bdg-clr-dis: #{$clr-pri-dis}; + --bit-bdg-clr-dis-text: #{$clr-pri-dis-text}; } .bit-bdg-sec { --bit-bdg-clr: #{$clr-sec}; --bit-bdg-clr-txt: #{$clr-sec-text}; + --bit-bdg-clr-dis: #{$clr-sec-dis}; + --bit-bdg-clr-dis-text: #{$clr-sec-dis-text}; } .bit-bdg-ter { --bit-bdg-clr: #{$clr-ter}; --bit-bdg-clr-txt: #{$clr-ter-text}; + --bit-bdg-clr-dis: #{$clr-ter-dis}; + --bit-bdg-clr-dis-text: #{$clr-ter-dis-text}; } .bit-bdg-inf { --bit-bdg-clr: #{$clr-inf}; --bit-bdg-clr-txt: #{$clr-inf-text}; + --bit-bdg-clr-dis: #{$clr-inf-dis}; + --bit-bdg-clr-dis-text: #{$clr-inf-dis-text}; } .bit-bdg-suc { --bit-bdg-clr: #{$clr-suc}; --bit-bdg-clr-txt: #{$clr-suc-text}; + --bit-bdg-clr-dis: #{$clr-suc-dis}; + --bit-bdg-clr-dis-text: #{$clr-suc-dis-text}; } .bit-bdg-wrn { --bit-bdg-clr: #{$clr-wrn}; --bit-bdg-clr-txt: #{$clr-wrn-text}; + --bit-bdg-clr-dis: #{$clr-wrn-dis}; + --bit-bdg-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-bdg-swr { --bit-bdg-clr: #{$clr-swr}; --bit-bdg-clr-txt: #{$clr-swr-text}; + --bit-bdg-clr-dis: #{$clr-swr-dis}; + --bit-bdg-clr-dis-text: #{$clr-swr-dis-text}; } .bit-bdg-err { --bit-bdg-clr: #{$clr-err}; --bit-bdg-clr-txt: #{$clr-err-text}; + --bit-bdg-clr-dis: #{$clr-err-dis}; + --bit-bdg-clr-dis-text: #{$clr-err-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Tag/BitTag.scss b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Tag/BitTag.scss index dba1b9dab6..9effece144 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Tag/BitTag.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Notifications/Tag/BitTag.scss @@ -18,7 +18,6 @@ &.bit-dis { cursor: default; user-select: none; - color: $clr-fg-dis; pointer-events: none; } } @@ -45,8 +44,9 @@ background-color: var(--bit-tag-clr); &.bit-dis { - border-color: $clr-brd-dis; - background-color: $clr-bg-dis; + color: var(--bit-tag-clr-dis-text); + border-color: var(--bit-tag-clr-dis); + background-color: var(--bit-tag-clr-dis); } } @@ -56,7 +56,8 @@ background-color: transparent; &.bit-dis { - border-color: $clr-brd-dis; + color: var(--bit-tag-clr-dis-text); + border-color: var(--bit-tag-clr-dis); background-color: transparent; } } @@ -67,6 +68,7 @@ background-color: transparent; &.bit-dis { + color: var(--bit-tag-clr-dis-text); border-color: transparent; background-color: transparent; } @@ -75,86 +77,120 @@ .bit-tag-pri { --bit-tag-clr: #{$clr-pri}; --bit-tag-clr-txt: #{$clr-pri-text}; + --bit-tag-clr-dis: #{$clr-pri-dis}; + --bit-tag-clr-dis-text: #{$clr-pri-dis-text}; } .bit-tag-sec { --bit-tag-clr: #{$clr-sec}; --bit-tag-clr-txt: #{$clr-sec-text}; + --bit-tag-clr-dis: #{$clr-sec-dis}; + --bit-tag-clr-dis-text: #{$clr-sec-dis-text}; } .bit-tag-ter { --bit-tag-clr: #{$clr-ter}; --bit-tag-clr-txt: #{$clr-ter-text}; + --bit-tag-clr-dis: #{$clr-ter-dis}; + --bit-tag-clr-dis-text: #{$clr-ter-dis-text}; } .bit-tag-inf { --bit-tag-clr: #{$clr-inf}; --bit-tag-clr-txt: #{$clr-inf-text}; + --bit-tag-clr-dis: #{$clr-inf-dis}; + --bit-tag-clr-dis-text: #{$clr-inf-dis-text}; } .bit-tag-suc { --bit-tag-clr: #{$clr-suc}; --bit-tag-clr-txt: #{$clr-suc-text}; + --bit-tag-clr-dis: #{$clr-suc-dis}; + --bit-tag-clr-dis-text: #{$clr-suc-dis-text}; } .bit-tag-wrn { --bit-tag-clr: #{$clr-wrn}; --bit-tag-clr-txt: #{$clr-wrn-text}; + --bit-tag-clr-dis: #{$clr-wrn-dis}; + --bit-tag-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-tag-swr { --bit-tag-clr: #{$clr-swr}; --bit-tag-clr-txt: #{$clr-swr-text}; + --bit-tag-clr-dis: #{$clr-swr-dis}; + --bit-tag-clr-dis-text: #{$clr-swr-dis-text}; } .bit-tag-err { --bit-tag-clr: #{$clr-err}; --bit-tag-clr-txt: #{$clr-err-text}; + --bit-tag-clr-dis: #{$clr-err-dis}; + --bit-tag-clr-dis-text: #{$clr-err-dis-text}; } .bit-tag-pbg { --bit-tag-clr: #{$clr-bg-pri}; --bit-tag-clr-txt: #{$clr-fg-pri}; + --bit-tag-clr-dis: #{$clr-bg-pri-dis}; + --bit-tag-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-tag-sbg { --bit-tag-clr: #{$clr-bg-sec}; --bit-tag-clr-txt: #{$clr-fg-pri}; + --bit-tag-clr-dis: #{$clr-bg-sec-dis}; + --bit-tag-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-tag-tbg { --bit-tag-clr: #{$clr-bg-ter}; --bit-tag-clr-txt: #{$clr-fg-pri}; + --bit-tag-clr-dis: #{$clr-bg-ter-dis}; + --bit-tag-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-tag-pfg { --bit-tag-clr: #{$clr-fg-pri}; --bit-tag-clr-txt: #{$clr-bg-pri}; + --bit-tag-clr-dis: #{$clr-fg-pri-dis}; + --bit-tag-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-tag-sfg { --bit-tag-clr: #{$clr-fg-sec}; --bit-tag-clr-txt: #{$clr-bg-pri}; + --bit-tag-clr-dis: #{$clr-fg-sec-dis}; + --bit-tag-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-tag-tfg { --bit-tag-clr: #{$clr-fg-ter}; --bit-tag-clr-txt: #{$clr-bg-pri}; + --bit-tag-clr-dis: #{$clr-fg-ter-dis}; + --bit-tag-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-tag-pbr { --bit-tag-clr: #{$clr-brd-pri}; --bit-tag-clr-txt: #{$clr-fg-pri}; + --bit-tag-clr-dis: #{$clr-brd-pri-dis}; + --bit-tag-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-tag-sbr { --bit-tag-clr: #{$clr-brd-sec}; --bit-tag-clr-txt: #{$clr-fg-pri}; + --bit-tag-clr-dis: #{$clr-brd-sec-dis}; + --bit-tag-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-tag-tbr { --bit-tag-clr: #{$clr-brd-ter}; --bit-tag-clr-txt: #{$clr-fg-pri}; + --bit-tag-clr-dis: #{$clr-brd-ter-dis}; + --bit-tag-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss index ad9a4a9583..ee4f143c0d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Icon/BitIcon.scss @@ -7,7 +7,7 @@ border-width: $shp-border-width; &.bit-dis { - color: $clr-fg-dis; + color: var(--bit-ico-clr-dis-text); } } @@ -16,12 +16,24 @@ color: var(--bit-ico-clr-txt); border-color: var(--bit-ico-clr); background-color: var(--bit-ico-clr); + + &.bit-dis { + color: var(--bit-ico-clr-dis-text); + border-color: var(--bit-ico-clr-dis); + background-color: var(--bit-ico-clr-dis); + } } .bit-ico-out { color: var(--bit-ico-clr); border-color: var(--bit-ico-clr); background-color: transparent; + + &.bit-dis { + color: var(--bit-ico-clr-dis-text); + border-color: var(--bit-ico-clr-dis); + background-color: transparent; + } } .bit-ico-txt { @@ -30,92 +42,130 @@ background-color: transparent; border: none; padding: 0; + + &.bit-dis { + color: var(--bit-ico-clr-dis-text); + } } .bit-ico-pri { --bit-ico-clr: #{$clr-pri}; --bit-ico-clr-txt: #{$clr-pri-text}; + --bit-ico-clr-dis: #{$clr-pri-dis}; + --bit-ico-clr-dis-text: #{$clr-pri-dis-text}; } .bit-ico-sec { --bit-ico-clr: #{$clr-sec}; --bit-ico-clr-txt: #{$clr-sec-text}; + --bit-ico-clr-dis: #{$clr-sec-dis}; + --bit-ico-clr-dis-text: #{$clr-sec-dis-text}; } .bit-ico-ter { --bit-ico-clr: #{$clr-ter}; --bit-ico-clr-txt: #{$clr-ter-text}; + --bit-ico-clr-dis: #{$clr-ter-dis}; + --bit-ico-clr-dis-text: #{$clr-ter-dis-text}; } .bit-ico-inf { --bit-ico-clr: #{$clr-inf}; --bit-ico-clr-txt: #{$clr-inf-text}; + --bit-ico-clr-dis: #{$clr-inf-dis}; + --bit-ico-clr-dis-text: #{$clr-inf-dis-text}; } .bit-ico-suc { --bit-ico-clr: #{$clr-suc}; --bit-ico-clr-txt: #{$clr-suc-text}; + --bit-ico-clr-dis: #{$clr-suc-dis}; + --bit-ico-clr-dis-text: #{$clr-suc-dis-text}; } .bit-ico-wrn { --bit-ico-clr: #{$clr-wrn}; --bit-ico-clr-txt: #{$clr-wrn-text}; + --bit-ico-clr-dis: #{$clr-wrn-dis}; + --bit-ico-clr-dis-text: #{$clr-wrn-dis-text}; } .bit-ico-swr { --bit-ico-clr: #{$clr-swr}; --bit-ico-clr-txt: #{$clr-swr-text}; + --bit-ico-clr-dis: #{$clr-swr-dis}; + --bit-ico-clr-dis-text: #{$clr-swr-dis-text}; } .bit-ico-err { --bit-ico-clr: #{$clr-err}; --bit-ico-clr-txt: #{$clr-err-text}; + --bit-ico-clr-dis: #{$clr-err-dis}; + --bit-ico-clr-dis-text: #{$clr-err-dis-text}; } .bit-ico-pbg { --bit-ico-clr: #{$clr-bg-pri}; --bit-ico-clr-txt: #{$clr-fg-pri}; + --bit-ico-clr-dis: #{$clr-bg-pri-dis}; + --bit-ico-clr-dis-text: #{$clr-bg-pri-dis-text}; } .bit-ico-sbg { --bit-ico-clr: #{$clr-bg-sec}; --bit-ico-clr-txt: #{$clr-fg-sec}; + --bit-ico-clr-dis: #{$clr-bg-sec-dis}; + --bit-ico-clr-dis-text: #{$clr-bg-sec-dis-text}; } .bit-ico-tbg { --bit-ico-clr: #{$clr-bg-ter}; --bit-ico-clr-txt: #{$clr-fg-ter}; + --bit-ico-clr-dis: #{$clr-bg-ter-dis}; + --bit-ico-clr-dis-text: #{$clr-bg-ter-dis-text}; } .bit-ico-pfg { --bit-ico-clr: #{$clr-fg-pri}; --bit-ico-clr-txt: #{$clr-bg-pri}; + --bit-ico-clr-dis: #{$clr-fg-pri-dis}; + --bit-ico-clr-dis-text: #{$clr-fg-pri-dis-text}; } .bit-ico-sfg { --bit-ico-clr: #{$clr-fg-sec}; --bit-ico-clr-txt: #{$clr-bg-sec}; + --bit-ico-clr-dis: #{$clr-fg-sec-dis}; + --bit-ico-clr-dis-text: #{$clr-fg-sec-dis-text}; } .bit-ico-tfg { --bit-ico-clr: #{$clr-fg-ter}; --bit-ico-clr-txt: #{$clr-bg-ter}; + --bit-ico-clr-dis: #{$clr-fg-ter-dis}; + --bit-ico-clr-dis-text: #{$clr-fg-ter-dis-text}; } .bit-ico-pbr { - --bit-ico-clr: #{$clr-bg-pri}; + --bit-ico-clr: #{$clr-brd-pri}; --bit-ico-clr-txt: #{$clr-fg-pri}; + --bit-ico-clr-dis: #{$clr-brd-pri-dis}; + --bit-ico-clr-dis-text: #{$clr-brd-pri-dis-text}; } .bit-ico-sbr { - --bit-ico-clr: #{$clr-bg-sec}; + --bit-ico-clr: #{$clr-brd-sec}; --bit-ico-clr-txt: #{$clr-fg-sec}; + --bit-ico-clr-dis: #{$clr-brd-sec-dis}; + --bit-ico-clr-dis-text: #{$clr-brd-sec-dis-text}; } .bit-ico-tbr { - --bit-ico-clr: #{$clr-bg-ter}; + --bit-ico-clr: #{$clr-brd-ter}; --bit-ico-clr-txt: #{$clr-fg-ter}; + --bit-ico-clr-dis: #{$clr-brd-ter-dis}; + --bit-ico-clr-dis-text: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.scss b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.scss index 1402f2c809..937de66c5f 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.scss +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/Link/BitLink.scss @@ -11,6 +11,7 @@ color: var(--bit-lnk-clr); font-family: $tg-font-family; background-color: transparent; + border-radius: $shp-border-radius; &:hover, &:active, @@ -18,6 +19,10 @@ color: var(--bit-lnk-clr-hover); } + &:focus-visible { + @include focus-ring(var(--bit-lnk-clr-focus)); + } + &:hover, &:active { text-decoration: underline; @@ -25,12 +30,16 @@ &.bit-dis { cursor: default; - color: $clr-fg-dis; + color: var(--bit-lnk-clr-dis); &:hover, &:active, &:focus { - color: $clr-fg-dis; + color: var(--bit-lnk-clr-dis); + } + + &:focus-visible { + box-shadow: none; } &:hover, @@ -73,84 +82,118 @@ .bit-lnk-pri { --bit-lnk-clr: #{$clr-pri}; --bit-lnk-clr-hover: #{$clr-pri-hover}; + --bit-lnk-clr-focus: #{$clr-pri-focus}; + --bit-lnk-clr-dis: #{$clr-pri-dis-text}; } .bit-lnk-sec { --bit-lnk-clr: #{$clr-sec}; --bit-lnk-clr-hover: #{$clr-sec-hover}; + --bit-lnk-clr-focus: #{$clr-sec-focus}; + --bit-lnk-clr-dis: #{$clr-sec-dis-text}; } .bit-lnk-ter { --bit-lnk-clr: #{$clr-ter}; --bit-lnk-clr-hover: #{$clr-ter-hover}; + --bit-lnk-clr-focus: #{$clr-ter-focus}; + --bit-lnk-clr-dis: #{$clr-ter-dis-text}; } .bit-lnk-inf { --bit-lnk-clr: #{$clr-inf}; --bit-lnk-clr-hover: #{$clr-inf-hover}; + --bit-lnk-clr-focus: #{$clr-inf-focus}; + --bit-lnk-clr-dis: #{$clr-inf-dis-text}; } .bit-lnk-suc { --bit-lnk-clr: #{$clr-suc}; --bit-lnk-clr-hover: #{$clr-suc-hover}; + --bit-lnk-clr-focus: #{$clr-suc-focus}; + --bit-lnk-clr-dis: #{$clr-suc-dis-text}; } .bit-lnk-wrn { --bit-lnk-clr: #{$clr-wrn}; --bit-lnk-clr-hover: #{$clr-wrn-hover}; + --bit-lnk-clr-focus: #{$clr-wrn-focus}; + --bit-lnk-clr-dis: #{$clr-wrn-dis-text}; } .bit-lnk-swr { --bit-lnk-clr: #{$clr-swr}; --bit-lnk-clr-hover: #{$clr-swr-hover}; + --bit-lnk-clr-focus: #{$clr-swr-focus}; + --bit-lnk-clr-dis: #{$clr-swr-dis-text}; } .bit-lnk-err { --bit-lnk-clr: #{$clr-err}; --bit-lnk-clr-hover: #{$clr-err-hover}; + --bit-lnk-clr-focus: #{$clr-err-focus}; + --bit-lnk-clr-dis: #{$clr-err-dis-text}; } .bit-lnk-pbg { --bit-lnk-clr: #{$clr-bg-pri}; --bit-lnk-clr-hover: #{$clr-bg-pri-hover}; + --bit-lnk-clr-focus: #{$clr-bg-pri-focus}; + --bit-lnk-clr-dis: #{$clr-bg-pri-dis-text}; } .bit-lnk-sbg { --bit-lnk-clr: #{$clr-bg-sec}; --bit-lnk-clr-hover: #{$clr-bg-sec-hover}; + --bit-lnk-clr-focus: #{$clr-bg-sec-focus}; + --bit-lnk-clr-dis: #{$clr-bg-sec-dis-text}; } .bit-lnk-tbg { --bit-lnk-clr: #{$clr-bg-ter}; --bit-lnk-clr-hover: #{$clr-bg-ter-hover}; + --bit-lnk-clr-focus: #{$clr-bg-ter-focus}; + --bit-lnk-clr-dis: #{$clr-bg-ter-dis-text}; } .bit-lnk-pfg { --bit-lnk-clr: #{$clr-fg-pri}; --bit-lnk-clr-hover: #{$clr-fg-pri-hover}; + --bit-lnk-clr-focus: #{$clr-fg-pri-focus}; + --bit-lnk-clr-dis: #{$clr-fg-pri-dis-text}; } .bit-lnk-sfg { --bit-lnk-clr: #{$clr-fg-sec}; --bit-lnk-clr-hover: #{$clr-fg-sec-hover}; + --bit-lnk-clr-focus: #{$clr-fg-sec-focus}; + --bit-lnk-clr-dis: #{$clr-fg-sec-dis-text}; } .bit-lnk-tfg { --bit-lnk-clr: #{$clr-fg-ter}; --bit-lnk-clr-hover: #{$clr-fg-ter-hover}; + --bit-lnk-clr-focus: #{$clr-fg-ter-focus}; + --bit-lnk-clr-dis: #{$clr-fg-ter-dis-text}; } .bit-lnk-pbr { --bit-lnk-clr: #{$clr-brd-pri}; --bit-lnk-clr-hover: #{$clr-brd-pri-hover}; + --bit-lnk-clr-focus: #{$clr-brd-pri-focus}; + --bit-lnk-clr-dis: #{$clr-brd-pri-dis-text}; } .bit-lnk-sbr { --bit-lnk-clr: #{$clr-brd-sec}; --bit-lnk-clr-hover: #{$clr-brd-sec-hover}; + --bit-lnk-clr-focus: #{$clr-brd-sec-focus}; + --bit-lnk-clr-dis: #{$clr-brd-sec-dis-text}; } .bit-lnk-tbr { --bit-lnk-clr: #{$clr-brd-ter}; --bit-lnk-clr-hover: #{$clr-brd-ter-hover}; + --bit-lnk-clr-focus: #{$clr-brd-ter-focus}; + --bit-lnk-clr-dis: #{$clr-brd-ter-dis-text}; } diff --git a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/MediaQuery/BitMediaQuery.razor.cs b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/MediaQuery/BitMediaQuery.razor.cs index ce88ddde5a..fdd9a8f5e4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Components/Utilities/MediaQuery/BitMediaQuery.razor.cs +++ b/src/BlazorUI/Bit.BlazorUI/Components/Utilities/MediaQuery/BitMediaQuery.razor.cs @@ -87,24 +87,24 @@ private static string GetQuery(BitScreenQuery? query) { return query switch { - BitScreenQuery.Xs => "(max-width: 600px)", - BitScreenQuery.Sm => "(min-width: 601px) and (max-width: 960px)", - BitScreenQuery.Md => "(min-width: 961px) and (max-width: 1280px)", - BitScreenQuery.Lg => "(min-width: 1281px) and (max-width: 1920px)", - BitScreenQuery.Xl => "(min-width: 1921px) and (max-width: 2560px)", - BitScreenQuery.Xxl => "(min-width: 2561px)", - - BitScreenQuery.LtSm => "(max-width: 600px)", - BitScreenQuery.LtMd => "(max-width: 960px)", - BitScreenQuery.LtLg => "(max-width: 1280px)", - BitScreenQuery.LtXl => "(max-width: 1920px)", - BitScreenQuery.LtXxl => "(max-width: 2560px)", - - BitScreenQuery.GtXs => "(min-width: 601px)", - BitScreenQuery.GtSm => "(min-width: 961px)", - BitScreenQuery.GtMd => "(min-width: 1281px)", - BitScreenQuery.GtLg => "(min-width: 1921px)", - BitScreenQuery.GtXl => "(min-width: 2561px)", + BitScreenQuery.Xs => "(max-width: 599px)", + BitScreenQuery.Sm => "(min-width: 600px) and (max-width: 959px)", + BitScreenQuery.Md => "(min-width: 960px) and (max-width: 1279px)", + BitScreenQuery.Lg => "(min-width: 1280px) and (max-width: 1919px)", + BitScreenQuery.Xl => "(min-width: 1920px) and (max-width: 2559px)", + BitScreenQuery.Xxl => "(min-width: 2560px)", + + BitScreenQuery.LtSm => "(max-width: 599px)", + BitScreenQuery.LtMd => "(max-width: 959px)", + BitScreenQuery.LtLg => "(max-width: 1279px)", + BitScreenQuery.LtXl => "(max-width: 1919px)", + BitScreenQuery.LtXxl => "(max-width: 2559px)", + + BitScreenQuery.GtXs => "(min-width: 600px)", + BitScreenQuery.GtSm => "(min-width: 960px)", + BitScreenQuery.GtMd => "(min-width: 1280px)", + BitScreenQuery.GtLg => "(min-width: 1920px)", + BitScreenQuery.GtXl => "(min-width: 2560px)", _ => string.Empty }; } diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/IBitBlazorUIServiceCollectionExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/IBitBlazorUIServiceCollectionExtensions.cs index 7cfa447bf2..3ff8668c67 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/IBitBlazorUIServiceCollectionExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/IBitBlazorUIServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; namespace Bit.BlazorUI; @@ -7,7 +8,23 @@ public static class IBitBlazorUIServiceCollectionExtensions { public static IServiceCollection AddBitBlazorUIServices(this IServiceCollection services) { - services.TryAddScoped(); + services.TryAddScoped(sp => + new BitThemeNotifications(sp.GetService())); + + // BitThemeJsNotifierReceiver is internal (consumers should listen on + // BitThemeNotifications.ThemeChanged), but DI still resolves it for us. + services.TryAddScoped(sp => + new BitThemeJsNotifierReceiver( + sp.GetRequiredService(), + sp.GetService())); + + services.TryAddScoped(sp => + new BitThemeManager( + sp.GetRequiredService(), + sp.GetRequiredService(), + sp.GetService())); + + services.TryAddScoped(); services.TryAddScoped(); diff --git a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs index ad822d6354..16d06c54ec 100644 --- a/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs +++ b/src/BlazorUI/Bit.BlazorUI/Extensions/JsInterop/IJSRuntimeExtensions.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; namespace Bit.BlazorUI; @@ -7,6 +8,11 @@ public static class IJSRuntimeExtensions { public const DynamicallyAccessedMemberTypes JsonSerialized = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties; + // Cache the per-runtime-type "is this runtime invalid?" probe. The probe is resolved once per + // concrete IJSRuntime type via reflection (the framework exposes no public API for this) and + // reused thereafter, so the reflection cost is paid once rather than on every interop call. + private static readonly ConcurrentDictionary> RuntimeInvalidProbes = new(); + /// @@ -78,14 +84,47 @@ public static bool IsRuntimeInvalid(this IJSRuntime jsRuntime) { if (jsRuntime is null) return false; - var type = jsRuntime.GetType(); + // Resolve (and cache) a probe for this concrete runtime type. The probe is defensive: it + // relies on framework-internal type names / members that can shift between .NET releases, + // so any missing member or reflection failure is treated as "runtime is valid" (return + // false) rather than throwing — a wrong-but-safe answer that lets the interop call proceed + // and surface a real error, instead of crashing here. + var probe = RuntimeInvalidProbes.GetOrAdd(jsRuntime.GetType(), BuildRuntimeInvalidProbe); + return probe(jsRuntime); + } - return type.Name switch + [SuppressMessage("Trimming", "IL2070:'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method.", Justification = "Framework-internal members probed reflectively; failures fall back to 'valid'.")] + private static Func BuildRuntimeInvalidProbe(Type type) + { + switch (type.Name) { - "UnsupportedJavaScriptRuntime" => true, // Prerendering - "RemoteJSRuntime" => (bool)type.GetProperty("IsInitialized")!.GetValue(jsRuntime)! is false, // Blazor server - "WebViewJSRuntime" => type.GetField("_ipcSender", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(jsRuntime) is null, // Blazor Hybrid - _ => false // Blazor WASM - }; + case "UnsupportedJavaScriptRuntime": // Prerendering + return static _ => true; + + case "RemoteJSRuntime": // Blazor Server + { + var isInitialized = type.GetProperty("IsInitialized", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (isInitialized is null) return static _ => false; + return rt => + { + try { return isInitialized.GetValue(rt) is false; } + catch { return false; } + }; + } + + case "WebViewJSRuntime": // Blazor Hybrid + { + var ipcSender = type.GetField("_ipcSender", BindingFlags.NonPublic | BindingFlags.Instance); + if (ipcSender is null) return static _ => false; + return rt => + { + try { return ipcSender.GetValue(rt) is null; } + catch { return false; } + }; + } + + default: // Blazor WASM and anything else: assume valid. + return static _ => false; + } } } diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-dark.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-dark.scss index 991b0d46cb..80b0c72361 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-dark.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-dark.scss @@ -9,3 +9,9 @@ @import "shapes.fluent.scss"; @import "typography.fluent.scss"; + +@import "motion.fluent"; + +@import "forced-colors.fluent"; + +@import "semantic-tokens.fluent"; diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-light.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-light.scss index b91212ae0c..e1af5ee056 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-light.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent-light.scss @@ -9,3 +9,9 @@ @import "shapes.fluent.scss"; @import "typography.fluent.scss"; + +@import "motion.fluent"; + +@import "forced-colors.fluent"; + +@import "semantic-tokens.fluent"; diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent.scss index e01dddb9de..203b521173 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/bit.blazorui.fluent.scss @@ -13,3 +13,9 @@ @import "shapes.fluent.scss"; @import "typography.fluent.scss"; + +@import "motion.fluent"; + +@import "forced-colors.fluent"; + +@import "semantic-tokens.fluent"; diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss index a9cf715ed3..9c419262bc 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-dark.scss @@ -12,6 +12,8 @@ --bit-clr-pri-light-hover: #78B2FF; --bit-clr-pri-light-active: #67A9FF; --bit-clr-pri-text: #212121; + --bit-clr-pri-dis: #2A4A6B; + --bit-clr-pri-dis-text: #4A7AAF; //secondary --bit-clr-sec: #FF9E67; --bit-clr-sec-hover: #E58E5D; @@ -23,6 +25,8 @@ --bit-clr-sec-light-hover: #FFB185; --bit-clr-sec-light-active: #FFA876; --bit-clr-sec-text: #212121; + --bit-clr-sec-dis: #5C3A22; + --bit-clr-sec-dis-text: #A06840; //tertiary --bit-clr-ter: #E6E6E6; --bit-clr-ter-hover: #CFCFCF; @@ -34,6 +38,8 @@ --bit-clr-ter-light-hover: #EBEBEB; --bit-clr-ter-light-active: #E9E9E9; --bit-clr-ter-text: #212121; + --bit-clr-ter-dis: #3D3D3D; + --bit-clr-ter-dis-text: #6B6B6B; //info --bit-clr-inf: #ABACAE; --bit-clr-inf-hover: #9A9B9D; @@ -45,6 +51,8 @@ --bit-clr-inf-light-hover: #BCBDBE; --bit-clr-inf-light-active: #B3B4B6; --bit-clr-inf-text: #212121; + --bit-clr-inf-dis: #3A3D40; + --bit-clr-inf-dis-text: #6B7078; //success --bit-clr-suc: #7FB58A; --bit-clr-suc-hover: #72A37C; @@ -56,6 +64,8 @@ --bit-clr-suc-light-hover: #99C4A1; --bit-clr-suc-light-active: #8CBC96; --bit-clr-suc-text: #212121; + --bit-clr-suc-dis: #2A4A2E; + --bit-clr-suc-dis-text: #4A8050; //warning --bit-clr-wrn: #FFD07A; --bit-clr-wrn-hover: #E5BB6E; @@ -67,6 +77,8 @@ --bit-clr-wrn-light-hover: #FFD995; --bit-clr-wrn-light-active: #FFD587; --bit-clr-wrn-text: #212121; + --bit-clr-wrn-dis: #5C4A1F; + --bit-clr-wrn-dis-text: #A08540; //severe-warning --bit-clr-swr: #E8A874; --bit-clr-swr-hover: #D19768; @@ -78,6 +90,8 @@ --bit-clr-swr-light-hover: #EDB990; --bit-clr-swr-light-active: #EAB182; --bit-clr-swr-text: #212121; + --bit-clr-swr-dis: #5C3A1F; + --bit-clr-swr-dis-text: #A06830; //error --bit-clr-err: #DC7C7C; --bit-clr-err-hover: #C67070; @@ -89,6 +103,8 @@ --bit-clr-err-light-hover: #E39696; --bit-clr-err-light-active: #E08989; --bit-clr-err-text: #212121; + --bit-clr-err-dis: #5C2A2A; + --bit-clr-err-dis-text: #A05050; //foregrounds --bit-clr-fg-pri: #F6F7F8; --bit-clr-fg-pri-hover: #DEDFE0; @@ -99,6 +115,8 @@ --bit-clr-fg-pri-light: #FFFFFF; --bit-clr-fg-pri-light-hover: #EDEDED; --bit-clr-fg-pri-light-active: #DCDCDC; + --bit-clr-fg-pri-dis: #4A4A4A; + --bit-clr-fg-pri-dis-text: #3A3A3A; --bit-clr-fg-sec: #ADB5BF; --bit-clr-fg-sec-hover: #9DA4AD; --bit-clr-fg-sec-active: #8B9199; @@ -108,6 +126,8 @@ --bit-clr-fg-sec-light: #B8C1CC; --bit-clr-fg-sec-light-hover: #A8B1BB; --bit-clr-fg-sec-light-active: #979DA7; + --bit-clr-fg-sec-dis: #3D4450; + --bit-clr-fg-sec-dis-text: #2E343D; --bit-clr-fg-ter: #616973; --bit-clr-fg-ter-hover: #565D66; --bit-clr-fg-ter-active: #4B5159; @@ -117,6 +137,8 @@ --bit-clr-fg-ter-light: #717881; --bit-clr-fg-ter-light-hover: #636A72; --bit-clr-fg-ter-light-active: #575D64; + --bit-clr-fg-ter-dis: #2E333A; + --bit-clr-fg-ter-dis-text: #22272D; --bit-clr-fg-dis: #666; //backgrounds --bit-clr-bg-pri: #010409; @@ -128,6 +150,8 @@ --bit-clr-bg-pri-light: #030914; --bit-clr-bg-pri-light-hover: #151C24; --bit-clr-bg-pri-light-active: #111A24; + --bit-clr-bg-pri-dis: #0A0E13; + --bit-clr-bg-pri-dis-text: #3A3A3A; --bit-clr-bg-sec: #101419; --bit-clr-bg-sec-hover: #1C222B; --bit-clr-bg-sec-active: #202731; @@ -137,6 +161,8 @@ --bit-clr-bg-sec-light: #1A1F26; --bit-clr-bg-sec-light-hover: #242A33; --bit-clr-bg-sec-light-active: #2B323D; + --bit-clr-bg-sec-dis: #141920; + --bit-clr-bg-sec-dis-text: #3A3A3A; --bit-clr-bg-ter: #181F26; --bit-clr-bg-ter-hover: #1B232B; --bit-clr-bg-ter-active: #202A33; @@ -146,6 +172,8 @@ --bit-clr-bg-ter-light: #202830; --bit-clr-bg-ter-light-hover: #212A33; --bit-clr-bg-ter-light-active: #27313B; + --bit-clr-bg-ter-dis: #1C232B; + --bit-clr-bg-ter-dis-text: #3A3A3A; --bit-clr-bg-dis: #222; --bit-clr-bg-overlay: rgba(255, 255, 255, 0.2); //borders @@ -158,6 +186,8 @@ --bit-clr-brd-pri-light: #78838E; --bit-clr-brd-pri-light-hover: #A1AFBD; --bit-clr-brd-pri-light-active: #B5C3D3; + --bit-clr-brd-pri-dis: #3A4250; + --bit-clr-brd-pri-dis-text: #4A5260; --bit-clr-brd-sec: #38424D; --bit-clr-brd-sec-hover: #5E6F80; --bit-clr-brd-sec-active: #718599; @@ -167,6 +197,8 @@ --bit-clr-brd-sec-light: #424C57; --bit-clr-brd-sec-light-hover: #68798A; --bit-clr-brd-sec-light-active: #7E91A4; + --bit-clr-brd-sec-dis: #252D36; + --bit-clr-brd-sec-dis-text: #3A4250; --bit-clr-brd-ter: #232933; --bit-clr-brd-ter-hover: #465266; --bit-clr-brd-ter-active: #586780; @@ -176,7 +208,31 @@ --bit-clr-brd-ter-light: #2E343E; --bit-clr-brd-ter-light-hover: #505B6F; --bit-clr-brd-ter-light-active: #637189; + --bit-clr-brd-ter-dis: #1A1F27; + --bit-clr-brd-ter-dis-text: #2A3038; --bit-clr-brd-dis: #21262D; + //req --bit-clr-req: #A4262C; + + //focus (a11y) — color of the focus indicator per role. + //Aliased to the role's base color so they track theme/brand overrides. + //Override these directly to differentiate focus from base color. + --bit-clr-pri-focus: var(--bit-clr-pri); + --bit-clr-sec-focus: var(--bit-clr-sec); + --bit-clr-ter-focus: var(--bit-clr-ter); + --bit-clr-inf-focus: var(--bit-clr-inf); + --bit-clr-suc-focus: var(--bit-clr-suc); + --bit-clr-wrn-focus: var(--bit-clr-wrn); + --bit-clr-swr-focus: var(--bit-clr-swr); + --bit-clr-err-focus: var(--bit-clr-err); + --bit-clr-fg-pri-focus: var(--bit-clr-fg-pri); + --bit-clr-fg-sec-focus: var(--bit-clr-fg-sec); + --bit-clr-fg-ter-focus: var(--bit-clr-fg-ter); + --bit-clr-bg-pri-focus: var(--bit-clr-bg-pri); + --bit-clr-bg-sec-focus: var(--bit-clr-bg-sec); + --bit-clr-bg-ter-focus: var(--bit-clr-bg-ter); + --bit-clr-brd-pri-focus: var(--bit-clr-brd-pri); + --bit-clr-brd-sec-focus: var(--bit-clr-brd-sec); + --bit-clr-brd-ter-focus: var(--bit-clr-brd-ter); } diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss index 8932a59828..9fd953c15d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/colors.fluent-light.scss @@ -14,6 +14,8 @@ --bit-clr-pri-light-hover: #8CC3EC; --bit-clr-pri-light-active: #76B6E8; --bit-clr-pri-text: #FFFFFF; + --bit-clr-pri-dis: #A3CFEF; + --bit-clr-pri-dis-text: #8BBDE5; //secondary --bit-clr-sec: #FF7D33; --bit-clr-sec-hover: #F27730; @@ -25,6 +27,8 @@ --bit-clr-sec-light-hover: #FFB185; --bit-clr-sec-light-active: #FFA470; --bit-clr-sec-text: #FFFFFF; + --bit-clr-sec-dis: #FFCFB3; + --bit-clr-sec-dis-text: #FFAD80; //tertiary --bit-clr-ter: #434343; --bit-clr-ter-hover: #3C3C3C; @@ -36,6 +40,8 @@ --bit-clr-ter-light-hover: #8E8E8E; --bit-clr-ter-light-active: #7B7B7B; --bit-clr-ter-text: #FFFFFF; + --bit-clr-ter-dis: #B8B8B8; + --bit-clr-ter-dis-text: #9E9E9E; //info --bit-clr-inf: #888F95; --bit-clr-inf-hover: #81888E; @@ -47,6 +53,8 @@ --bit-clr-inf-light-hover: #B8BCBF; --bit-clr-inf-light-active: #ACB1B5; --bit-clr-inf-text: #FFFFFF; + --bit-clr-inf-dis: #C3C7CA; + --bit-clr-inf-dis-text: #ACB1B5; //success --bit-clr-suc: #49A94C; --bit-clr-suc-hover: #45A148; @@ -58,6 +66,8 @@ --bit-clr-suc-light-hover: #92CB94; --bit-clr-suc-light-active: #80C382; --bit-clr-suc-text: #FFFFFF; + --bit-clr-suc-dis: #B5DDB7; + --bit-clr-suc-dis-text: #8CC98F; //warning --bit-clr-wrn: #F2B01F; --bit-clr-wrn-hover: #E6A71D; @@ -69,6 +79,8 @@ --bit-clr-wrn-light-hover: #F7D079; --bit-clr-wrn-light-active: #F6C862; --bit-clr-wrn-text: #FFFFFF; + --bit-clr-wrn-dis: #FBDFAA; + --bit-clr-wrn-dis-text: #F5C75E; //severe-warning --bit-clr-swr: #D47E30; --bit-clr-swr-hover: #C9782E; @@ -80,6 +92,8 @@ --bit-clr-swr-light-hover: #DD9859; --bit-clr-swr-light-active: #DA914F; --bit-clr-swr-text: #FFFFFF; + --bit-clr-swr-dis: #EABB8E; + --bit-clr-swr-dis-text: #D9A06A; //error --bit-clr-err: #DF3A2E; --bit-clr-err-hover: #D4372C; @@ -91,6 +105,8 @@ --bit-clr-err-light-hover: #E56158; --bit-clr-err-light-active: #E4584D; --bit-clr-err-text: #FFFFFF; + --bit-clr-err-dis: #F0ADA8; + --bit-clr-err-dis-text: #E8827A; //foreground --bit-clr-fg-pri: #1A1A1A; --bit-clr-fg-pri-hover: #111111; @@ -101,6 +117,8 @@ --bit-clr-fg-pri-light: #262626; --bit-clr-fg-pri-light-hover: #191919; --bit-clr-fg-pri-light-active: #111111; + --bit-clr-fg-pri-dis: #8A8A8A; + --bit-clr-fg-pri-dis-text: #BEBEBE; --bit-clr-fg-sec: #5A5A5A; --bit-clr-fg-sec-hover: #444444; --bit-clr-fg-sec-active: #333333; @@ -110,6 +128,8 @@ --bit-clr-fg-sec-light: #484848; --bit-clr-fg-sec-light-hover: #515151; --bit-clr-fg-sec-light-active: #414141; + --bit-clr-fg-sec-dis: #A3A3A3; + --bit-clr-fg-sec-dis-text: #C8C8C8; --bit-clr-fg-ter: #999999; --bit-clr-fg-ter-hover: #666666; --bit-clr-fg-ter-active: #4D4D4D; @@ -119,6 +139,8 @@ --bit-clr-fg-ter-light: #A7A7A7; --bit-clr-fg-ter-light-hover: #747474; --bit-clr-fg-ter-light-active: #5A5A5A; + --bit-clr-fg-ter-dis: #C8C8C8; + --bit-clr-fg-ter-dis-text: #DADADA; --bit-clr-fg-dis: #AAAAAA; //backgrounds --bit-clr-bg-pri: #FFFFFF; @@ -130,6 +152,8 @@ --bit-clr-bg-pri-light: #FFFFFF; --bit-clr-bg-pri-light-hover: #F2F2F2; --bit-clr-bg-pri-light-active: #EBEBEB; + --bit-clr-bg-pri-dis: #F7F7F7; + --bit-clr-bg-pri-dis-text: #CCCCCC; --bit-clr-bg-sec: #F5F5F5; --bit-clr-bg-sec-hover: #F2F1F0; --bit-clr-bg-sec-active: #E8E7E6; @@ -139,6 +163,8 @@ --bit-clr-bg-sec-light: #F5F5F5; --bit-clr-bg-sec-light-hover: #F2F1F0; --bit-clr-bg-sec-light-active: #E8E7E6; + --bit-clr-bg-sec-dis: #F9F9F9; + --bit-clr-bg-sec-dis-text: #CCCCCC; --bit-clr-bg-ter: #EDECEB; --bit-clr-bg-ter-hover: #E5E5E4; --bit-clr-bg-ter-active: #E0DFDF; @@ -148,6 +174,8 @@ --bit-clr-bg-ter-light: #F1F1F1; --bit-clr-bg-ter-light-hover: #EDEDED; --bit-clr-bg-ter-light-active: #E6E6E6; + --bit-clr-bg-ter-dis: #F5F5F4; + --bit-clr-bg-ter-dis-text: #CCCCCC; --bit-clr-bg-dis: #F3F2F1; --bit-clr-bg-overlay: rgba(0, 0, 0, 0.4); //borders @@ -160,6 +188,8 @@ --bit-clr-brd-pri-light: #898989; --bit-clr-brd-pri-light-hover: #6C6B6B; --bit-clr-brd-pri-light-active: #535353; + --bit-clr-brd-pri-dis: #BEBEBE; + --bit-clr-brd-pri-dis-text: #CCCCCC; --bit-clr-brd-sec: #B0AFAE; --bit-clr-brd-sec-hover: #7D7C7C; --bit-clr-brd-sec-active: #636362; @@ -169,6 +199,8 @@ --bit-clr-brd-sec-light: #BFBFBF; --bit-clr-brd-sec-light-hover: #8A8A8A; --bit-clr-brd-sec-light-active: #727272; + --bit-clr-brd-sec-dis: #D8D8D7; + --bit-clr-brd-sec-dis-text: #CCCCCC; --bit-clr-brd-ter: #DEDDDC; --bit-clr-brd-ter-hover: #CBCBCB; --bit-clr-brd-ter-active: #B0B0B0; @@ -178,7 +210,31 @@ --bit-clr-brd-ter-light: #E9E9E9; --bit-clr-brd-ter-light-hover: #D7D7D7; --bit-clr-brd-ter-light-active: #C1C1C1; + --bit-clr-brd-ter-dis: #EEEDEC; + --bit-clr-brd-ter-dis-text: #CCCCCC; --bit-clr-brd-dis: #E5E3DF; + //req --bit-clr-req: #A4262C; + + //focus (a11y) — color of the focus indicator per role. + //Aliased to the role's base color so they track theme/brand overrides. + //Override these directly to differentiate focus from base color. + --bit-clr-pri-focus: var(--bit-clr-pri); + --bit-clr-sec-focus: var(--bit-clr-sec); + --bit-clr-ter-focus: var(--bit-clr-ter); + --bit-clr-inf-focus: var(--bit-clr-inf); + --bit-clr-suc-focus: var(--bit-clr-suc); + --bit-clr-wrn-focus: var(--bit-clr-wrn); + --bit-clr-swr-focus: var(--bit-clr-swr); + --bit-clr-err-focus: var(--bit-clr-err); + --bit-clr-fg-pri-focus: var(--bit-clr-fg-pri); + --bit-clr-fg-sec-focus: var(--bit-clr-fg-sec); + --bit-clr-fg-ter-focus: var(--bit-clr-fg-ter); + --bit-clr-bg-pri-focus: var(--bit-clr-bg-pri); + --bit-clr-bg-sec-focus: var(--bit-clr-bg-sec); + --bit-clr-bg-ter-focus: var(--bit-clr-bg-ter); + --bit-clr-brd-pri-focus: var(--bit-clr-brd-pri); + --bit-clr-brd-sec-focus: var(--bit-clr-brd-sec); + --bit-clr-brd-ter-focus: var(--bit-clr-brd-ter); } diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/forced-colors.fluent.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/forced-colors.fluent.scss new file mode 100644 index 0000000000..e2b040d3b6 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/forced-colors.fluent.scss @@ -0,0 +1,49 @@ +// Windows High Contrast / forced-colors: preserve readability with system palette. +@media (forced-colors: active) { + :root { + --bit-clr-fg-pri: CanvasText; + --bit-clr-fg-sec: CanvasText; + --bit-clr-fg-ter: CanvasText; + --bit-clr-bg-pri: Canvas; + --bit-clr-bg-sec: Canvas; + --bit-clr-bg-ter: Canvas; + --bit-clr-brd-pri: CanvasText; + --bit-clr-brd-sec: CanvasText; + --bit-clr-pri: LinkText; + --bit-clr-pri-hover: LinkText; + --bit-clr-pri-active: LinkText; + --bit-clr-pri-text: Canvas; + --bit-clr-err: CanvasText; + --bit-clr-suc: CanvasText; + --bit-clr-wrn: CanvasText; + --bit-shd-cal: none; + --bit-shd-sm: none; + --bit-shd-nm: none; + --bit-shd-md: none; + --bit-shd-lg: none; + + // Focus must remain visible in High Contrast — use system Highlight color and a single solid ring. + --bit-clr-pri-focus: Highlight; + --bit-clr-sec-focus: Highlight; + --bit-clr-ter-focus: Highlight; + --bit-clr-inf-focus: Highlight; + --bit-clr-suc-focus: Highlight; + --bit-clr-wrn-focus: Highlight; + --bit-clr-swr-focus: Highlight; + --bit-clr-err-focus: Highlight; + --bit-clr-fg-pri-focus: Highlight; + --bit-clr-bg-pri-focus: Canvas; + --bit-clr-brd-pri-focus: Highlight; + --bit-shd-focus-ring: 0 0 0 max(2px, var(--bit-shp-focus-ring-width)) Highlight; + } +} + +// Stronger contrast preference (non-forced): deepen borders and primary text slightly. +// Use a concrete value rather than self-referencing var(--bit-shp-brd-width, …) — a custom +// property cannot reference its own current value (the cycle resolves to the fallback per +// the CSS spec, which makes the intent unclear and brittle across engines). +@media (prefers-contrast: more) { + :root { + --bit-shp-brd-width: 1px; + } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/motion.fluent.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/motion.fluent.scss new file mode 100644 index 0000000000..2b0a55ccfd --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/motion.fluent.scss @@ -0,0 +1,15 @@ +:root { + --bit-mot-duration: 200ms; + --bit-mot-duration-short: 100ms; + --bit-mot-duration-long: 300ms; + --bit-mot-easing: cubic-bezier(0.33, 0, 0.67, 1); + --bit-layout-density-scale: 1; +} + +@media (prefers-reduced-motion: reduce) { + :root { + --bit-mot-duration: 0ms; + --bit-mot-duration-short: 0ms; + --bit-mot-duration-long: 0ms; + } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/semantic-tokens.fluent.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/semantic-tokens.fluent.scss new file mode 100644 index 0000000000..b00a3ec20c --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/semantic-tokens.fluent.scss @@ -0,0 +1,12 @@ +// Semantic aliases over primitive tokens — use in app CSS instead of raw --bit-clr-* where intent matters. +:root { + --bit-sem-surface-page: var(--bit-clr-bg-pri); + --bit-sem-surface-elevated: var(--bit-clr-bg-sec); + --bit-sem-surface-muted: var(--bit-clr-bg-ter); + --bit-sem-text-primary: var(--bit-clr-fg-pri); + --bit-sem-text-secondary: var(--bit-clr-fg-sec); + --bit-sem-border-default: var(--bit-clr-brd-pri); + --bit-sem-accent-primary: var(--bit-clr-pri); + --bit-sem-focus-ring: var(--bit-shd-focus-ring); + --bit-sem-focus-color: var(--bit-clr-pri-focus); +} diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/shapes.fluent.scss b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/shapes.fluent.scss index ab1287f1b6..a3ee6ea69d 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/shapes.fluent.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/Fluent/shapes.fluent.scss @@ -16,4 +16,15 @@ --bit-shp-brd-radius: 0.125rem; --bit-shp-brd-width: 0.0625rem; --bit-shp-brd-style: solid; + + //focus ring (a11y) — width is the visible thickness, offset is the gap between the element and the ring. + --bit-shp-focus-ring-width: 0.125rem; //2px + --bit-shp-focus-ring-offset: 0.125rem; //2px + //global focus shadow used by the focus-ring mixin and the .bit-css-shd-focus utility. + //Inner ring uses the page background to create a separator between the element and the colored ring (WCAG 2.4.11/2.4.13). + //The mixin sets --bit-focus-ring-color/--bit-focus-ring-offset locally on the focused element so per-call overrides apply. + //The forced-colors block replaces this whole token, bypassing the hooks, which is the intended behavior for High Contrast. + --bit-shd-focus-ring: + 0 0 0 var(--bit-focus-ring-offset, var(--bit-shp-focus-ring-offset)) var(--bit-clr-bg-pri-focus), + 0 0 0 calc(var(--bit-focus-ring-offset, var(--bit-shp-focus-ring-offset)) + var(--bit-shp-focus-ring-width)) var(--bit-focus-ring-color, var(--bit-clr-pri-focus)); } diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/bit-css.scss b/src/BlazorUI/Bit.BlazorUI/Styles/bit-css.scss index 5fbd89cc52..f6ce2614fc 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/bit-css.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/bit-css.scss @@ -939,3 +939,42 @@ .bit-css-shp-brd-style { border-style: $shp-border-style; } + + +//focus (a11y) — color tokens applied on :focus-visible. +.bit-css-clr-pri-focus:focus-visible { + color: $clr-pri-focus; +} + +.bit-css-clr-sec-focus:focus-visible { + color: $clr-sec-focus; +} + +.bit-css-clr-ter-focus:focus-visible { + color: $clr-ter-focus; +} + +.bit-css-clr-inf-focus:focus-visible { + color: $clr-inf-focus; +} + +.bit-css-clr-suc-focus:focus-visible { + color: $clr-suc-focus; +} + +.bit-css-clr-wrn-focus:focus-visible { + color: $clr-wrn-focus; +} + +.bit-css-clr-swr-focus:focus-visible { + color: $clr-swr-focus; +} + +.bit-css-clr-err-focus:focus-visible { + color: $clr-err-focus; +} + +//focus ring utility — drop-in class to apply the global, theme-aware focus indicator. +.bit-css-shd-focus-ring:focus-visible { + @include focus-ring; +} diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/functions.scss b/src/BlazorUI/Bit.BlazorUI/Styles/functions.scss index feee193b9f..1000ac2136 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/functions.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/functions.scss @@ -18,3 +18,49 @@ $html-font-size: 16px; @function spacing($spacingValue) { @return calc($spacing-scaling-factor * $spacingValue); } + +/* + * focus-ring($color, $offset) + * -------------------------------------------------- + * Renders a consistent, theme-aware focus indicator across components. + * + * Behavior: + * - Uses a two-layer box-shadow: an inner ring matching the page background + * (separator) and an outer colored ring (visible indicator). This satisfies + * WCAG 2.4.11 (Focus Not Obscured) and 2.4.13 (Focus Appearance). + * - Width is driven by --bit-shp-focus-ring-width and offset by --bit-shp-focus-ring-offset, + * both globally themable. + * - Color defaults to --bit-clr-pri-focus and accepts any role token (e.g. $clr-err-focus) + * so invalid/success/etc. variants can opt into matching focus colors. + * - In Windows High Contrast mode the global --bit-shd-focus-ring is overridden to a + * single solid Highlight ring; @see forced-colors.fluent.scss. + * + * Usage: + * .my-component { + * &:focus-visible { @include focus-ring; } + * &.bit-inv:focus-visible { @include focus-ring($clr-err-focus); } + * } + */ +@mixin focus-ring($color: $clr-pri-focus, $offset: $shp-focus-ring-offset) { + outline: none; // Replace native outline with the themed ring below. + --bit-focus-ring-color: #{$color}; + --bit-focus-ring-offset: #{$offset}; + + // Inline fallback mirrors the global --bit-shd-focus-ring expression in shapes.fluent.scss + // so focus indicators still render if a consumer-defined theme omits that token. + box-shadow: var(--bit-shd-focus-ring, + 0 0 0 var(--bit-focus-ring-offset, #{$offset}) var(--bit-clr-bg-pri-focus), + 0 0 0 calc(var(--bit-focus-ring-offset, #{$offset}) + #{$shp-focus-ring-width}) var(--bit-focus-ring-color, #{$color})); +} + +/* + * focus-underline-ring($color) + * -------------------------------------------------- + * Renders a focus indicator as a single bottom rule, used by "underlined" + * input variants (TextField/SearchBox/DatePicker .bit-*-und) where a full ring + * conflicts with the visual language of the component. + */ +@mixin focus-underline-ring($color: $clr-pri-focus) { + outline: none; + box-shadow: inset 0 calc(-1 * #{$shp-focus-ring-width}) 0 0 #{$color}; +} diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/media-queries.scss b/src/BlazorUI/Bit.BlazorUI/Styles/media-queries.scss index c1628fe3f8..ced80bd3d4 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/media-queries.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/media-queries.scss @@ -1,110 +1,120 @@ // https://github.com/Necromancerx/media-queries-scss-mixins -$brk-xs-max: 600px; -$brk-sm-min: 601px; -$brk-sm-max: 960px; -$brk-md-min: 961px; -$brk-md-max: 1280px; -$brk-lg-min: 1281px; -$brk-lg-max: 1920px; -$brk-xl-min: 1921px; -$brk-xl-max: 2560px; -$brk-xxl-min: 2561px; +$mq-xs-max: 599px; +$mq-sm-min: 600px; +$mq-sm-max: 959px; +$mq-md-min: 960px; +$mq-md-max: 1279px; +$mq-lg-min: 1280px; +$mq-lg-max: 1919px; +$mq-xl-min: 1920px; +$mq-xl-max: 2559px; +$mq-xxl-min: 2560px; + +:root { + --bit-bp-xs: 0; + --bit-bp-sm: #{$mq-sm-min}; + --bit-bp-md: #{$mq-md-min}; + --bit-bp-lg: #{$mq-lg-min}; + --bit-bp-xl: #{$mq-xl-min}; + --bit-bp-xxl: #{$mq-xxl-min}; +} // media devices @mixin xs { - @media screen and (max-width: #{$brk-xs-max}) { + @media screen and (max-width: #{$mq-xs-max}) { @content; } } + @mixin sm { - @media screen and (min-width: #{$brk-sm-min}) and (max-width: #{$brk-sm-max}) { + @media screen and (min-width: #{$mq-sm-min}) and (max-width: #{$mq-sm-max}) { @content; } } @mixin md { - @media screen and (min-width: #{$brk-md-min}) and (max-width: #{$brk-md-max}) { + @media screen and (min-width: #{$mq-md-min}) and (max-width: #{$mq-md-max}) { @content; } } @mixin lg { - @media screen and (min-width: #{$brk-lg-min}) and (max-width: #{$brk-lg-max}) { + @media screen and (min-width: #{$mq-lg-min}) and (max-width: #{$mq-lg-max}) { @content; } } @mixin xl { - @media screen and (min-width: #{$brk-xl-min}) and (max-width: #{$brk-xl-max}) { + @media screen and (min-width: #{$mq-xl-min}) and (max-width: #{$mq-xl-max}) { @content; } } @mixin xxl { - @media screen and (min-width: #{$brk-xxl-min}) { + @media screen and (min-width: #{$mq-xxl-min}) { @content; } } // media lt queries @mixin lt-sm { - @media screen and (max-width: #{$brk-xs-max}) { + @media screen and (max-width: #{$mq-xs-max}) { @content; } } @mixin lt-md { - @media screen and (max-width: #{$brk-sm-max}) { + @media screen and (max-width: #{$mq-sm-max}) { @content; } } @mixin lt-lg { - @media screen and (max-width: #{$brk-md-max}) { + @media screen and (max-width: #{$mq-md-max}) { @content; } } @mixin lt-xl { - @media screen and (max-width: #{$brk-lg-max}) { + @media screen and (max-width: #{$mq-lg-max}) { @content; } } @mixin lt-xxl { - @media screen and (max-width: #{$brk-xl-max}) { + @media screen and (max-width: #{$mq-xl-max}) { @content; } } // media gt queries @mixin gt-xs { - @media screen and (min-width: #{$brk-sm-min}) { + @media screen and (min-width: #{$mq-sm-min}) { @content; } } @mixin gt-sm { - @media screen and (min-width: #{$brk-md-min}) { + @media screen and (min-width: #{$mq-md-min}) { @content; } } @mixin gt-md { - @media screen and (min-width: #{$brk-lg-min}) { + @media screen and (min-width: #{$mq-lg-min}) { @content; } } @mixin gt-lg { - @media screen and (min-width: #{$brk-xl-min}) { + @media screen and (min-width: #{$mq-xl-min}) { @content; } } @mixin gt-xl { - @media screen and (min-width: #{$brk-xxl-min}) { + @media screen and (min-width: #{$mq-xxl-min}) { @content; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Styles/theme-variables.scss b/src/BlazorUI/Bit.BlazorUI/Styles/theme-variables.scss index 66c82ab061..55de2c55f2 100644 --- a/src/BlazorUI/Bit.BlazorUI/Styles/theme-variables.scss +++ b/src/BlazorUI/Bit.BlazorUI/Styles/theme-variables.scss @@ -105,6 +105,8 @@ $clr-fg-pri-dark-active: var(--bit-clr-fg-pri-dark-active); $clr-fg-pri-light: var(--bit-clr-fg-pri-light); $clr-fg-pri-light-hover: var(--bit-clr-fg-pri-light-hover); $clr-fg-pri-light-active: var(--bit-clr-fg-pri-light-active); +$clr-fg-pri-dis: var(--bit-clr-fg-pri-dis); +$clr-fg-pri-dis-text: var(--bit-clr-fg-pri-dis-text); $clr-fg-sec: var(--bit-clr-fg-sec); $clr-fg-sec-hover: var(--bit-clr-fg-sec-hover); $clr-fg-sec-active: var(--bit-clr-fg-sec-active); @@ -114,6 +116,8 @@ $clr-fg-sec-dark-active: var(--bit-clr-fg-sec-dark-active); $clr-fg-sec-light: var(--bit-clr-fg-sec-light); $clr-fg-sec-light-hover: var(--bit-clr-fg-sec-light-hover); $clr-fg-sec-light-active: var(--bit-clr-fg-sec-light-active); +$clr-fg-sec-dis: var(--bit-clr-fg-sec-dis); +$clr-fg-sec-dis-text: var(--bit-clr-fg-sec-dis-text); $clr-fg-ter: var(--bit-clr-fg-ter); $clr-fg-ter-hover: var(--bit-clr-fg-ter-hover); $clr-fg-ter-active: var(--bit-clr-fg-ter-active); @@ -123,6 +127,8 @@ $clr-fg-ter-dark-active: var(--bit-clr-fg-ter-dark-active); $clr-fg-ter-light: var(--bit-clr-fg-ter-light); $clr-fg-ter-light-hover: var(--bit-clr-fg-ter-light-hover); $clr-fg-ter-light-active: var(--bit-clr-fg-ter-light-active); +$clr-fg-ter-dis: var(--bit-clr-fg-ter-dis); +$clr-fg-ter-dis-text: var(--bit-clr-fg-ter-dis-text); $clr-fg-dis: var(--bit-clr-fg-dis); //backgrounds @@ -135,6 +141,8 @@ $clr-bg-pri-dark-active: var(--bit-clr-bg-pri-dark-active); $clr-bg-pri-light: var(--bit-clr-bg-pri-light); $clr-bg-pri-light-hover: var(--bit-clr-bg-pri-light-hover); $clr-bg-pri-light-active: var(--bit-clr-bg-pri-light-active); +$clr-bg-pri-dis: var(--bit-clr-bg-pri-dis); +$clr-bg-pri-dis-text: var(--bit-clr-bg-pri-dis-text); $clr-bg-sec: var(--bit-clr-bg-sec); $clr-bg-sec-hover: var(--bit-clr-bg-sec-hover); $clr-bg-sec-active: var(--bit-clr-bg-sec-active); @@ -144,6 +152,8 @@ $clr-bg-sec-dark-active: var(--bit-clr-bg-sec-dark-active); $clr-bg-sec-light: var(--bit-clr-bg-sec-light); $clr-bg-sec-light-hover: var(--bit-clr-bg-sec-light-hover); $clr-bg-sec-light-active: var(--bit-clr-bg-sec-light-active); +$clr-bg-sec-dis: var(--bit-clr-bg-sec-dis); +$clr-bg-sec-dis-text: var(--bit-clr-bg-sec-dis-text); $clr-bg-ter: var(--bit-clr-bg-ter); $clr-bg-ter-hover: var(--bit-clr-bg-ter-hover); $clr-bg-ter-active: var(--bit-clr-bg-ter-active); @@ -153,6 +163,8 @@ $clr-bg-ter-dark-active: var(--bit-clr-bg-ter-dark-active); $clr-bg-ter-light: var(--bit-clr-bg-ter-light); $clr-bg-ter-light-hover: var(--bit-clr-bg-ter-light-hover); $clr-bg-ter-light-active: var(--bit-clr-bg-ter-light-active); +$clr-bg-ter-dis: var(--bit-clr-bg-ter-dis); +$clr-bg-ter-dis-text: var(--bit-clr-bg-ter-dis-text); $clr-bg-dis: var(--bit-clr-bg-dis); $clr-bg-overlay: var(--bit-clr-bg-overlay); @@ -166,6 +178,8 @@ $clr-brd-pri-dark-active: var(--bit-clr-brd-pri-dark-active); $clr-brd-pri-light: var(--bit-clr-brd-pri-light); $clr-brd-pri-light-hover: var(--bit-clr-brd-pri-light-hover); $clr-brd-pri-light-active: var(--bit-clr-brd-pri-light-active); +$clr-brd-pri-dis: var(--bit-clr-brd-pri-dis); +$clr-brd-pri-dis-text: var(--bit-clr-brd-pri-dis-text); $clr-brd-sec: var(--bit-clr-brd-sec); $clr-brd-sec-hover: var(--bit-clr-brd-sec-hover); $clr-brd-sec-active: var(--bit-clr-brd-sec-active); @@ -175,6 +189,8 @@ $clr-brd-sec-dark-active: var(--bit-clr-brd-sec-dark-active); $clr-brd-sec-light: var(--bit-clr-brd-sec-light); $clr-brd-sec-light-hover: var(--bit-clr-brd-sec-light-hover); $clr-brd-sec-light-active: var(--bit-clr-brd-sec-light-active); +$clr-brd-sec-dis: var(--bit-clr-brd-sec-dis); +$clr-brd-sec-dis-text: var(--bit-clr-brd-sec-dis-text); $clr-brd-ter: var(--bit-clr-brd-ter); $clr-brd-ter-hover: var(--bit-clr-brd-ter-hover); $clr-brd-ter-active: var(--bit-clr-brd-ter-active); @@ -184,11 +200,50 @@ $clr-brd-ter-dark-active: var(--bit-clr-brd-ter-dark-active); $clr-brd-ter-light: var(--bit-clr-brd-ter-light); $clr-brd-ter-light-hover: var(--bit-clr-brd-ter-light-hover); $clr-brd-ter-light-active: var(--bit-clr-brd-ter-light-active); +$clr-brd-ter-dis: var(--bit-clr-brd-ter-dis); +$clr-brd-ter-dis-text: var(--bit-clr-brd-ter-dis-text); $clr-brd-dis: var(--bit-clr-brd-dis); //required $clr-req: var(--bit-clr-req); +//disabled — per-role disabled colors +$clr-pri-dis: var(--bit-clr-pri-dis); +$clr-pri-dis-text: var(--bit-clr-pri-dis-text); +$clr-sec-dis: var(--bit-clr-sec-dis); +$clr-sec-dis-text: var(--bit-clr-sec-dis-text); +$clr-ter-dis: var(--bit-clr-ter-dis); +$clr-ter-dis-text: var(--bit-clr-ter-dis-text); +$clr-inf-dis: var(--bit-clr-inf-dis); +$clr-inf-dis-text: var(--bit-clr-inf-dis-text); +$clr-suc-dis: var(--bit-clr-suc-dis); +$clr-suc-dis-text: var(--bit-clr-suc-dis-text); +$clr-wrn-dis: var(--bit-clr-wrn-dis); +$clr-wrn-dis-text: var(--bit-clr-wrn-dis-text); +$clr-swr-dis: var(--bit-clr-swr-dis); +$clr-swr-dis-text: var(--bit-clr-swr-dis-text); +$clr-err-dis: var(--bit-clr-err-dis); +$clr-err-dis-text: var(--bit-clr-err-dis-text); + +//focus (a11y) — per-role focus indicator color, plus shape/shadow tokens consumed by the focus-ring mixin. +$clr-pri-focus: var(--bit-clr-pri-focus); +$clr-sec-focus: var(--bit-clr-sec-focus); +$clr-ter-focus: var(--bit-clr-ter-focus); +$clr-inf-focus: var(--bit-clr-inf-focus); +$clr-suc-focus: var(--bit-clr-suc-focus); +$clr-wrn-focus: var(--bit-clr-wrn-focus); +$clr-swr-focus: var(--bit-clr-swr-focus); +$clr-err-focus: var(--bit-clr-err-focus); +$clr-fg-pri-focus: var(--bit-clr-fg-pri-focus); +$clr-fg-sec-focus: var(--bit-clr-fg-sec-focus); +$clr-fg-ter-focus: var(--bit-clr-fg-ter-focus); +$clr-bg-pri-focus: var(--bit-clr-bg-pri-focus); +$clr-bg-sec-focus: var(--bit-clr-bg-sec-focus); +$clr-bg-ter-focus: var(--bit-clr-bg-ter-focus); +$clr-brd-pri-focus: var(--bit-clr-brd-pri-focus); +$clr-brd-sec-focus: var(--bit-clr-brd-sec-focus); +$clr-brd-ter-focus: var(--bit-clr-brd-ter-focus); + //neutrals $clr-ntr-white: var(--bit-clr-ntr-white); $clr-ntr-black: var(--bit-clr-ntr-black); @@ -268,6 +323,11 @@ $zindex-base: var(--bit-zin-base); $shp-border-radius: var(--bit-shp-brd-radius); $shp-border-width: var(--bit-shp-brd-width); $shp-border-style: var(--bit-shp-brd-style); +$shp-focus-ring-width: var(--bit-shp-focus-ring-width); +$shp-focus-ring-offset: var(--bit-shp-focus-ring-offset); + +//shadows-focus +$box-shadow-focus-ring: var(--bit-shd-focus-ring); /*---- Typography ----*/ $tg-font-family: var(--bit-tpg-font-family); diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitAccentColorPresets.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitAccentColorPresets.cs new file mode 100644 index 0000000000..6330c87143 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitAccentColorPresets.cs @@ -0,0 +1,14 @@ +namespace Bit.BlazorUI; + +/// +/// Sample accent (primary) hex values for branding — assign to or derive via . +/// +public static class BitAccentColorPresets +{ + public const string Blue = "#1A86D8"; + public const string Purple = "#8764B8"; + public const string Green = "#107C10"; + public const string Orange = "#CA5010"; + public const string Teal = "#038387"; + public const string Rose = "#C239B3"; +} diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitExternalThemeJsExtensions.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitExternalThemeJsExtensions.cs new file mode 100644 index 0000000000..e1643b94fc --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitExternalThemeJsExtensions.cs @@ -0,0 +1,14 @@ +namespace Bit.BlazorUI; + +internal static class BitExternalThemeJsExtensions +{ + internal static ValueTask BitExternalThemeAttach(this IJSRuntime js, string linkElementId, string href) + { + return js.InvokeVoid("BitBlazorUI.ExternalTheme.attach", linkElementId, href); + } + + internal static ValueTask BitExternalThemeDetach(this IJSRuntime js, string linkElementId) + { + return js.InvokeVoid("BitBlazorUI.ExternalTheme.detach", linkElementId); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitExternalThemeLoader.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitExternalThemeLoader.cs new file mode 100644 index 0000000000..d0230e0711 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitExternalThemeLoader.cs @@ -0,0 +1,107 @@ +namespace Bit.BlazorUI; + +/// +/// Loads optional alternate CSS bundles by id (creates or updates a <link rel="stylesheet">). +/// Use only trusted/same-origin URLs. The loader rejects relative-with-scheme strings such as +/// javascript: and data:, rejects protocol-relative URLs (//host/...); +/// for absolute URLs the scheme must be http or https. +/// +public sealed class BitExternalThemeLoader +{ + private readonly IJSRuntime _js; + + public BitExternalThemeLoader(IJSRuntime js) + { + ArgumentNullException.ThrowIfNull(js); + + _js = js; + } + + /// + /// Creates (or updates) a <link rel="stylesheet"> element identified by + /// with the given . + /// + /// DOM id used to find / create the link element. + /// Stylesheet URL. Same-origin paths and absolute http/https URLs are accepted; non-http schemes are rejected. + public ValueTask AttachStylesheetAsync(string linkElementId, string href) + { + ArgumentException.ThrowIfNullOrWhiteSpace(linkElementId); + ArgumentException.ThrowIfNullOrWhiteSpace(href); + + // Defense-in-depth href validation. The XML doc warns the caller to use trusted URLs, but + // the JS side does no checking, so a tampered or user-supplied href could attach a + // `javascript:`/`data:` link that browsers may actually load when probed. Rejecting them + // here keeps the loader honest. Protocol-relative URLs (e.g. `//host/x.css`) are also + // rejected because the browser resolves them as cross-origin http(s), which violates the + // documented "same-origin relative path or absolute http/https URL" contract. Relative + // paths (no scheme) are allowed because that's the common same-origin case + // ("/css/dark.css", "themes/light.css"). + ValidateHref(href); + + return _js.BitExternalThemeAttach(linkElementId, href); + } + + /// Removes a previously-attached <link rel="stylesheet"> by id. + /// DOM id originally passed to . + public ValueTask DetachStylesheetAsync(string linkElementId) + { + ArgumentException.ThrowIfNullOrWhiteSpace(linkElementId); + + return _js.BitExternalThemeDetach(linkElementId); + } + + private static void ValidateHref(string href) + { + // Protocol-relative URLs (e.g. "//evil.example/x.css") are not absolute URIs as far as + // Uri.TryCreate(Absolute) is concerned, so they would otherwise slip past the scheme + // check below and reach the browser, which resolves them to a cross-origin + // http(s)://evil.example/x.css. Reject them up front; callers must use a same-origin + // relative path or an explicit http/https URL. Browsers also normalize backslashes to + // forward slashes in URLs, so `\\host\x.css` is the same attack surface — block it too. + var trimmed = href.TrimStart(); + if (trimmed.StartsWith("//", StringComparison.Ordinal) || + trimmed.StartsWith(@"\\", StringComparison.Ordinal) || + trimmed.StartsWith(@"/\", StringComparison.Ordinal) || + trimmed.StartsWith(@"\/", StringComparison.Ordinal)) + { + throw new ArgumentException( + "Stylesheet href must not be a protocol-relative URL. Use a same-origin relative path or an explicit http/https URL.", + nameof(href)); + } + + if (Uri.TryCreate(href, UriKind.Absolute, out var absolute)) + { + // Reject every absolute-URL scheme other than http(s). That covers javascript:, data:, + // file:, vbscript:, blob:, etc. — none of which belong on a . + // Uri.UriSchemeHttp/Https are static readonly fields, not constants, so they can't be + // used in a relational pattern; compare with case-insensitive ordinal equality instead. + if (!string.Equals(absolute.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && + !string.Equals(absolute.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException( + $"Stylesheet href scheme '{absolute.Scheme}' is not allowed. Use http, https, or a same-origin relative path.", + nameof(href)); + } + + return; + } + + // Relative URLs are allowed. We still want to reject the `javascript:` form even when + // Uri.TryCreate refuses to parse it as absolute (it does for some inputs depending on + // platform), so a final string-prefix sanity check covers the residual surface. + if (LooksLikeDangerousRelativeScheme(trimmed)) + { + throw new ArgumentException( + "Stylesheet href appears to use a non-http scheme (javascript:, data:, vbscript:). Use http, https, or a same-origin relative path.", + nameof(href)); + } + } + + private static bool LooksLikeDangerousRelativeScheme(string trimmed) + { + // Match common attack vectors that browsers used to (or still) accept on attribute values. + return trimmed.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase) + || trimmed.StartsWith("data:", StringComparison.OrdinalIgnoreCase) + || trimmed.StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme.ts b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme.ts new file mode 100644 index 0000000000..7a42d13ed1 --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitTheme.ts @@ -0,0 +1,448 @@ +// Attribute / storage names — kept aligned with BitThemeAttributeNames.cs and BitThemeSsr.cs in +// C#. If you rename a constant here, mirror the change there (the contract test under +// Bit.BlazorUI.Tests.Utils.Theme will catch a mismatch). +namespace BitBlazorUI { + const ATTR_THEME = 'bit-theme'; + const ATTR_THEME_DEFAULT = 'bit-theme-default'; + const ATTR_THEME_SYSTEM = 'bit-theme-system'; + const ATTR_THEME_PERSIST = 'bit-theme-persist'; + const ATTR_THEME_PERSIST_COOKIE = 'bit-theme-persist-cookie'; + const ATTR_THEME_DARK = 'bit-theme-dark'; + const ATTR_THEME_LIGHT = 'bit-theme-light'; + const STORAGE_KEY = 'bit-current-theme'; + // Kept aligned with BitThemeCookie.PreferenceCookieName in C#. When cookie persistence is + // enabled, the client mirrors the persisted preference into this cookie so the server can read + // the user's current choice (via BitThemeSsr.BuildRootThemeAttributes) and paint the matching + // theme on first server render — keeping the client (localStorage) and server (cookie) stores + // in sync instead of drifting apart. + const COOKIE_NAME = 'bit-theme-preference'; + // ~400 days, the upper bound modern browsers clamp persistent cookies to. + const COOKIE_MAX_AGE_SECONDS = 34560000; + + type onThemeChangeType = (newThemeName: string, oldThemeName: string) => void; + + export interface ThemeOptions { + system?: boolean; + persist?: boolean; + /** Mirror the persisted preference into the COOKIE_NAME cookie so SSR stays in sync with client choices. */ + persistCookie?: boolean; + theme?: string | null; + default?: string | null; + darkTheme?: string | null; + lightTheme?: string | null; + onChange?: onThemeChangeType; + } + + interface ThemeSetOptions { + /** When true, startup init so resolved light/dark does not disable OS sync from bit-theme-system. */ + fromInit?: boolean; + /** Reserved for internal OS refresh paths (same as normal set without touching follow-system flags). */ + internalOsRefresh?: boolean; + } + + export class Theme { + private static SYSTEM_THEME = 'system'; + private static THEME_ATTRIBUTE = ATTR_THEME; + private static THEME_STORAGE_KEY = STORAGE_KEY; + + private static _persist = false; + private static _persistCookie = false; + private static _darkTheme: string = 'dark'; + private static _lightTheme: string = 'light'; + private static _initOptions: ThemeOptions = {}; + private static _currentTheme = Theme._lightTheme; + private static _onThemeChange: onThemeChangeType = () => { }; + + /** When true, user pinned an explicit theme via set (not system); disables following OS until set('system'). */ + private static _stopFollowingSystem = false; + + /** + * When true, the user explicitly opted into following the OS at runtime (e.g. via useSystem()), + * so we follow OS changes even without a persisted "system" preference or the bit-theme-system attribute. + * Cleared automatically when the user pins a concrete theme via set(...). + */ + private static _runtimeFollowSystem = false; + + private static _schemeMediaQuery: MediaQueryList | null = null; + private static _onSchemeChange = () => Theme.applyResolvedSystemThemeFromOs(); + + private static _dotnetNotifier: DotNetObject | null = null; + + private static _appliedVarKeys = new WeakMap(); + + public static init(options: ThemeOptions) { + Object.assign(Theme._initOptions, options); + + let deferPersist = false; + + if (Theme._initOptions.onChange) { + Theme._onThemeChange = Theme._initOptions.onChange; + } + + if (Theme._initOptions.darkTheme) { + Theme._darkTheme = Theme._initOptions.darkTheme; + } + + if (Theme._initOptions.lightTheme) { + Theme._lightTheme = Theme._initOptions.lightTheme; + } + + // Cookie mirroring is independent of localStorage persistence: an SSR app may want the + // server to read the preference cookie without necessarily enabling localStorage. + if (Theme._initOptions.persistCookie) { + Theme._persistCookie = true; + } + + let theme = Theme._initOptions.theme || Theme._initOptions.default || Theme._lightTheme; + + // Resolve the first-paint theme with the SAME precedence as the SSR inline script in + // BitThemeSsr.cs (BuildInlineScriptBody): a `system` opt-in — the bit-theme-system + // attribute, the JS `system: true` option, or an explicit bit-theme="system" — follows + // the OS and takes precedence over an explicit bit-theme / bit-theme-default at first + // paint. A persisted preference (handled below) still wins over this. + // + // Previously this only resolved the OS theme when there was NO explicit theme/default, + // so a document with both bit-theme="..." and bit-theme-system painted the explicit + // theme on hydration while the SSR script painted the OS-resolved one — a flash of the + // wrong theme. Keeping the two code paths in lockstep avoids that. + if (Theme._initOptions.system || Theme._initOptions.theme === Theme.SYSTEM_THEME) { + theme = Theme.isSystemDark() ? Theme._darkTheme : Theme._lightTheme; + } + + if (Theme._initOptions.persist) { + Theme._persist = true; + const persisted = Theme.getPersisted(); if (persisted) { + theme = persisted; + // An explicit persisted preset (anything other than "system") means the user pinned a theme; + // stop following the OS even when is present. + Theme._stopFollowingSystem = persisted !== Theme.SYSTEM_THEME; + } else if (Theme._initOptions.system) { + // System mode is enabled but no value has been persisted yet. Avoid writing the + // resolved light/dark theme to storage during the initial set() — otherwise the next + // init would treat that concrete value as an explicit user choice and stop following + // the OS. Disable persistence for the initial set() and re-enable it afterwards so + // SYSTEM_THEME remains the effective persisted indicator until the user picks one. + Theme._persist = false; + deferPersist = true; + } + } + + // The JS `system: true` option must on its own enable OS follow, otherwise callers that + // configure system mode purely from JS (no attribute, no persisted + // "system" value) would never get the prefers-color-scheme listener attached because + // shouldFollowSystem() only considers the HTML attribute and persisted value. Runtime + // follow is still cleared when the user later pins a concrete theme via set(...). + if (Theme._initOptions.system && !Theme._stopFollowingSystem) { + Theme._runtimeFollowSystem = true; + } + + Theme.set(theme, { fromInit: true }); + + if (deferPersist) { + Theme._persist = true; + } + } + + public static onChange(fn: onThemeChangeType) { + Theme._onThemeChange = fn; + } + + public static get() { + // Report the theme that is actually applied: the bit-theme attribute is the source of + // truth (it is what is painted, and set()/OS-follow keep _currentTheme in sync with it). + // The persisted *preference* is a separate concept exposed via getPersisted(); the two + // can legitimately diverge — e.g. runtime OS-follow updates the attribute without + // rewriting storage — so get() must not substitute the persisted value here. + Theme._currentTheme = document.documentElement.getAttribute(Theme.THEME_ATTRIBUTE) || ''; + + return Theme._currentTheme; + } + + public static set(themeName: string, options?: ThemeSetOptions) { + // Reject null / undefined / empty inputs up-front so we never call setAttribute(...) with + // a value that coerces to the literal string "null" or "undefined". The non-null + // assertion below was unsafe because getActualTheme can return null for null input. + if (!themeName) return Theme._currentTheme; + + const fromInit = options?.fromInit === true; + const internalOs = options?.internalOsRefresh === true; + + if (!fromInit && !internalOs) { + if (themeName === Theme.SYSTEM_THEME) { + Theme._stopFollowingSystem = false; + Theme._runtimeFollowSystem = true; + } else { + Theme._stopFollowingSystem = true; + Theme._runtimeFollowSystem = false; + } + } + + const resolved = Theme.getActualTheme(themeName); + if (!resolved) return Theme._currentTheme; + Theme._currentTheme = resolved; + + if (Theme._persist) { + // localStorage can throw in Safari private mode, in iframes that block storage, + // when over quota, or under restrictive document policies (e.g. file:// in some + // browsers). Theme persistence is best-effort — never let it break theme switching. + try { + localStorage.setItem(Theme.THEME_STORAGE_KEY, themeName); + } catch { /* persistence unavailable; continue without storing */ } + } + + // Mirror the preference into the cookie so the server (BitThemeSsr.BuildRootThemeAttributes) + // can paint the matching theme on first render. Stored verbatim (the abstract key, e.g. + // "system" / "dark" / "fluent-light"), matching what localStorage holds. + if (Theme._persistCookie) { + Theme.writePreferenceCookie(themeName); + } + + const oldTheme = document.documentElement.getAttribute(Theme.THEME_ATTRIBUTE) || ''; + + document.documentElement.setAttribute(Theme.THEME_ATTRIBUTE, Theme._currentTheme); + + Theme.dispatchThemeChange(Theme._currentTheme, oldTheme); + + Theme.syncSystemThemeListener(); + + return Theme._currentTheme; + } + + public static toggleDarkLight() { + // Toggle relative to the configured dark theme: when the dark theme is active switch to + // light, otherwise switch to dark. Anchoring on the dark theme (rather than the light + // one) means a configured pair such as bit-theme-light="fluent-light" / + // bit-theme-dark="fluent-dark" toggles correctly in BOTH directions, and any other + // current value (a custom or unrecognized theme) resolves to the dark theme instead of + // silently collapsing to light. + Theme._currentTheme = Theme._currentTheme === Theme._darkTheme + ? Theme._lightTheme + : Theme._darkTheme; + + Theme.set(Theme._currentTheme); + + return Theme._currentTheme; + } + + /** Pins storage (when persist is on) to system and follows OS light/dark until an explicit preset is set. */ + public static useSystem() { + return Theme.set(Theme.SYSTEM_THEME); + } + + public static applyTheme(theme: Record, element?: HTMLElement) { + const el = element || document.body; + const keys = Object.keys(theme); + const prev = Theme._appliedVarKeys.get(el) || []; + prev.filter(key => !keys.includes(key)).forEach(key => el.style.removeProperty(key)); + keys.forEach(key => el.style.setProperty(key, theme[key])); + Theme._appliedVarKeys.set(el, keys); + } + + /** Removes --bit-* properties previously applied by applyTheme on the target (default document.body). */ + public static clearAppliedTheme(element?: HTMLElement) { + const el = element || document.body; + const keys = Theme._appliedVarKeys.get(el); + if (!keys || keys.length === 0) return; + keys.forEach(k => el.style.removeProperty(k)); + Theme._appliedVarKeys.delete(el); + } + + public static isSystemDark() { + return matchMedia('(prefers-color-scheme: dark)').matches; + } + + public static getPersisted() { + if (Theme._persist) { + // Mirror the write side: localStorage.getItem can throw under the same conditions as + // setItem (Safari private mode, blocked storage, etc.). Treat failure as "no persisted + // value" so the rest of the resolution chain (system / default / lightTheme) takes over. + try { + const stored = localStorage.getItem(Theme.THEME_STORAGE_KEY); + if (stored) return stored; + } catch { /* fall through to cookie / null */ } + } + + // Cookie-only persistence (or localStorage unavailable): read the same preference the + // server uses so the client and server agree on the stored choice. + if (Theme._persistCookie) { + return Theme.readPreferenceCookie(); + } + + return null; + } + + public static registerDotNetNotifier(dotNetRef: DotNetObject) { + Theme._dotnetNotifier = dotNetRef; + } + + public static unregisterDotNetNotifier() { + Theme._dotnetNotifier = null; + } + + private static writePreferenceCookie(value: string) { + try { + if (typeof document === 'undefined' || !value) return; + const secure = location.protocol === 'https:' ? '; Secure' : ''; + document.cookie = + `${COOKIE_NAME}=${encodeURIComponent(value)}; path=/; max-age=${COOKIE_MAX_AGE_SECONDS}; SameSite=Lax${secure}`; + } catch { /* cookies unavailable / blocked; best-effort like localStorage */ } + } + + private static readPreferenceCookie(): string | null { + try { + if (typeof document === 'undefined' || !document.cookie) return null; + const prefix = `${COOKIE_NAME}=`; + const match = document.cookie + .split(';') + .map(c => c.trim()) + .find(c => c.startsWith(prefix)); + return match ? decodeURIComponent(match.substring(prefix.length)) : null; + } catch { + return null; + } + } + + private static shouldFollowSystem(): boolean { + if (typeof document === 'undefined') return false; + if (Theme._stopFollowingSystem) return false; + // An explicitly persisted theme (anything other than SYSTEM_THEME) wins over the + // bit-theme-system attribute, otherwise a stale attribute could override the user's choice. + if (Theme._persist) { + const persisted = Theme.getPersisted(); + if (persisted && persisted !== Theme.SYSTEM_THEME) return false; + if (persisted === Theme.SYSTEM_THEME) return true; + } + if (Theme._runtimeFollowSystem) return true; + if (document.documentElement.hasAttribute(ATTR_THEME_SYSTEM)) return true; + return false; + } + + private static syncSystemThemeListener() { + Theme.detachSystemThemeListener(); + if (!Theme.shouldFollowSystem()) return; + Theme.attachSystemThemeListener(); + } + + private static attachSystemThemeListener() { + if (!window.matchMedia) return; + Theme._schemeMediaQuery = matchMedia('(prefers-color-scheme: dark)'); + const mq = Theme._schemeMediaQuery as MediaQueryList & { addListener?: (cb: () => void) => void }; + if (typeof mq.addEventListener === 'function') { + mq.addEventListener('change', Theme._onSchemeChange); + } else { + mq.addListener?.(Theme._onSchemeChange); + } + } + + private static detachSystemThemeListener() { + if (!Theme._schemeMediaQuery) return; + const mq = Theme._schemeMediaQuery as MediaQueryList & { removeListener?: (cb: () => void) => void }; + if (typeof mq.removeEventListener === 'function') { + mq.removeEventListener('change', Theme._onSchemeChange); + } else { + mq.removeListener?.(Theme._onSchemeChange); + } + Theme._schemeMediaQuery = null; + } + + private static applyResolvedSystemThemeFromOs() { + if (!Theme.shouldFollowSystem()) return; + + const resolved = Theme.isSystemDark() ? Theme._darkTheme : Theme._lightTheme; + const oldTheme = document.documentElement.getAttribute(Theme.THEME_ATTRIBUTE) || ''; + + if (resolved === oldTheme) return; + + Theme._currentTheme = resolved; + document.documentElement.setAttribute(Theme.THEME_ATTRIBUTE, resolved); + Theme.dispatchThemeChange(resolved, oldTheme); + } + + private static dispatchThemeChange(newTheme: string, oldTheme: string) { + Theme._onThemeChange?.(newTheme, oldTheme); + const n = Theme._dotnetNotifier; + if (n) { + // Swallow rejections so a disposed circuit / receiver does not surface as an + // unhandled promise rejection. Theme dispatch is fire-and-forget by design. + n.invokeMethodAsync('NotifyThemeChangedFromJs', newTheme, oldTheme) + .catch(() => { /* receiver gone or invocation failed; nothing actionable here */ }); + } + } + + private static getActualTheme(theme: string | null) { + if (theme === Theme.SYSTEM_THEME) { + return Theme.isSystemDark() ? Theme._darkTheme : Theme._lightTheme; + } + + return theme; + } + } + + /** Attach or swap alternate theme stylesheets at runtime (prefer same-origin / trusted URLs). */ + export class ExternalTheme { + private static validateHref(href: string) { + const trimmed = href?.trimStart(); + if (!trimmed) { + throw new Error('Stylesheet href is required.'); + } + if (trimmed.startsWith('//') || trimmed.startsWith('\\\\')) { + throw new Error('Stylesheet href must not be a protocol-relative URL.'); + } + if (/^javascript:/i.test(trimmed) || /^data:/i.test(trimmed) || /^vbscript:/i.test(trimmed)) { + throw new Error('Stylesheet href must not use a non-http scheme.'); + } + let url: URL; + try { + url = new URL(href, document.baseURI); + } catch { + throw new Error('Stylesheet href is not a valid URL.'); + } + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new Error(`Stylesheet href scheme '${url.protocol}' is not allowed.`); + } + if (url.origin !== location.origin) { + throw new Error('Stylesheet href must be same-origin.'); + } + } + + public static attach(linkId: string, href: string) { + ExternalTheme.validateHref(href); + + const existing = document.getElementById(linkId); + let link: HTMLLinkElement; + if (existing && existing.tagName === 'LINK') { + link = existing as HTMLLinkElement; + } else { + // No element, or an element with the same id but a different tag (e.g. a stale + // breakout defense-in-depth + case '\\': // CSS escape sequences + case '\0': + case '\n': + case '\r': + case '\f': // null / newlines / form feed + return true; + } + } + + // Comment markers could swallow trailing declarations in the inline-style concatenation + // (e.g. "red/*" eating the following ";--next:..." up to a later "*/"). + return value.Contains("/*", StringComparison.Ordinal) + || value.Contains("*/", StringComparison.Ordinal); } internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) @@ -376,6 +498,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Primary.LightHover = bitTheme.Color.Primary.LightHover ?? other.Color.Primary.LightHover; result.Color.Primary.LightActive = bitTheme.Color.Primary.LightActive ?? other.Color.Primary.LightActive; result.Color.Primary.Text = bitTheme.Color.Primary.Text ?? other.Color.Primary.Text; + result.Color.Primary.Disabled = bitTheme.Color.Primary.Disabled ?? other.Color.Primary.Disabled; + result.Color.Primary.DisabledText = bitTheme.Color.Primary.DisabledText ?? other.Color.Primary.DisabledText; + result.Color.Primary.Focus = bitTheme.Color.Primary.Focus ?? other.Color.Primary.Focus; result.Color.Secondary.Main = bitTheme.Color.Secondary.Main ?? other.Color.Secondary.Main; result.Color.Secondary.MainHover = bitTheme.Color.Secondary.MainHover ?? other.Color.Secondary.MainHover; @@ -387,6 +512,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Secondary.LightHover = bitTheme.Color.Secondary.LightHover ?? other.Color.Secondary.LightHover; result.Color.Secondary.LightActive = bitTheme.Color.Secondary.LightActive ?? other.Color.Secondary.LightActive; result.Color.Secondary.Text = bitTheme.Color.Secondary.Text ?? other.Color.Secondary.Text; + result.Color.Secondary.Disabled = bitTheme.Color.Secondary.Disabled ?? other.Color.Secondary.Disabled; + result.Color.Secondary.DisabledText = bitTheme.Color.Secondary.DisabledText ?? other.Color.Secondary.DisabledText; + result.Color.Secondary.Focus = bitTheme.Color.Secondary.Focus ?? other.Color.Secondary.Focus; result.Color.Tertiary.Main = bitTheme.Color.Tertiary.Main ?? other.Color.Tertiary.Main; result.Color.Tertiary.MainHover = bitTheme.Color.Tertiary.MainHover ?? other.Color.Tertiary.MainHover; @@ -398,6 +526,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Tertiary.LightHover = bitTheme.Color.Tertiary.LightHover ?? other.Color.Tertiary.LightHover; result.Color.Tertiary.LightActive = bitTheme.Color.Tertiary.LightActive ?? other.Color.Tertiary.LightActive; result.Color.Tertiary.Text = bitTheme.Color.Tertiary.Text ?? other.Color.Tertiary.Text; + result.Color.Tertiary.Disabled = bitTheme.Color.Tertiary.Disabled ?? other.Color.Tertiary.Disabled; + result.Color.Tertiary.DisabledText = bitTheme.Color.Tertiary.DisabledText ?? other.Color.Tertiary.DisabledText; + result.Color.Tertiary.Focus = bitTheme.Color.Tertiary.Focus ?? other.Color.Tertiary.Focus; result.Color.Info.Main = bitTheme.Color.Info.Main ?? other.Color.Info.Main; result.Color.Info.MainHover = bitTheme.Color.Info.MainHover ?? other.Color.Info.MainHover; @@ -409,6 +540,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Info.LightHover = bitTheme.Color.Info.LightHover ?? other.Color.Info.LightHover; result.Color.Info.LightActive = bitTheme.Color.Info.LightActive ?? other.Color.Info.LightActive; result.Color.Info.Text = bitTheme.Color.Info.Text ?? other.Color.Info.Text; + result.Color.Info.Disabled = bitTheme.Color.Info.Disabled ?? other.Color.Info.Disabled; + result.Color.Info.DisabledText = bitTheme.Color.Info.DisabledText ?? other.Color.Info.DisabledText; + result.Color.Info.Focus = bitTheme.Color.Info.Focus ?? other.Color.Info.Focus; result.Color.Success.Main = bitTheme.Color.Success.Main ?? other.Color.Success.Main; result.Color.Success.MainHover = bitTheme.Color.Success.MainHover ?? other.Color.Success.MainHover; @@ -420,6 +554,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Success.LightHover = bitTheme.Color.Success.LightHover ?? other.Color.Success.LightHover; result.Color.Success.LightActive = bitTheme.Color.Success.LightActive ?? other.Color.Success.LightActive; result.Color.Success.Text = bitTheme.Color.Success.Text ?? other.Color.Success.Text; + result.Color.Success.Disabled = bitTheme.Color.Success.Disabled ?? other.Color.Success.Disabled; + result.Color.Success.DisabledText = bitTheme.Color.Success.DisabledText ?? other.Color.Success.DisabledText; + result.Color.Success.Focus = bitTheme.Color.Success.Focus ?? other.Color.Success.Focus; result.Color.Warning.Main = bitTheme.Color.Warning.Main ?? other.Color.Warning.Main; result.Color.Warning.MainHover = bitTheme.Color.Warning.MainHover ?? other.Color.Warning.MainHover; @@ -431,6 +568,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Warning.LightHover = bitTheme.Color.Warning.LightHover ?? other.Color.Warning.LightHover; result.Color.Warning.LightActive = bitTheme.Color.Warning.LightActive ?? other.Color.Warning.LightActive; result.Color.Warning.Text = bitTheme.Color.Warning.Text ?? other.Color.Warning.Text; + result.Color.Warning.Disabled = bitTheme.Color.Warning.Disabled ?? other.Color.Warning.Disabled; + result.Color.Warning.DisabledText = bitTheme.Color.Warning.DisabledText ?? other.Color.Warning.DisabledText; + result.Color.Warning.Focus = bitTheme.Color.Warning.Focus ?? other.Color.Warning.Focus; result.Color.SevereWarning.Main = bitTheme.Color.SevereWarning.Main ?? other.Color.SevereWarning.Main; result.Color.SevereWarning.MainHover = bitTheme.Color.SevereWarning.MainHover ?? other.Color.SevereWarning.MainHover; @@ -442,6 +582,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.SevereWarning.LightHover = bitTheme.Color.SevereWarning.LightHover ?? other.Color.SevereWarning.LightHover; result.Color.SevereWarning.LightActive = bitTheme.Color.SevereWarning.LightActive ?? other.Color.SevereWarning.LightActive; result.Color.SevereWarning.Text = bitTheme.Color.SevereWarning.Text ?? other.Color.SevereWarning.Text; + result.Color.SevereWarning.Disabled = bitTheme.Color.SevereWarning.Disabled ?? other.Color.SevereWarning.Disabled; + result.Color.SevereWarning.DisabledText = bitTheme.Color.SevereWarning.DisabledText ?? other.Color.SevereWarning.DisabledText; + result.Color.SevereWarning.Focus = bitTheme.Color.SevereWarning.Focus ?? other.Color.SevereWarning.Focus; result.Color.Error.Main = bitTheme.Color.Error.Main ?? other.Color.Error.Main; result.Color.Error.MainHover = bitTheme.Color.Error.MainHover ?? other.Color.Error.MainHover; @@ -453,6 +596,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Error.LightHover = bitTheme.Color.Error.LightHover ?? other.Color.Error.LightHover; result.Color.Error.LightActive = bitTheme.Color.Error.LightActive ?? other.Color.Error.LightActive; result.Color.Error.Text = bitTheme.Color.Error.Text ?? other.Color.Error.Text; + result.Color.Error.Disabled = bitTheme.Color.Error.Disabled ?? other.Color.Error.Disabled; + result.Color.Error.DisabledText = bitTheme.Color.Error.DisabledText ?? other.Color.Error.DisabledText; + result.Color.Error.Focus = bitTheme.Color.Error.Focus ?? other.Color.Error.Focus; result.Color.Foreground.Primary = bitTheme.Color.Foreground.Primary ?? other.Color.Foreground.Primary; result.Color.Foreground.PrimaryHover = bitTheme.Color.Foreground.PrimaryHover ?? other.Color.Foreground.PrimaryHover; @@ -463,6 +609,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Foreground.PrimaryLight = bitTheme.Color.Foreground.PrimaryLight ?? other.Color.Foreground.PrimaryLight; result.Color.Foreground.PrimaryLightHover = bitTheme.Color.Foreground.PrimaryLightHover ?? other.Color.Foreground.PrimaryLightHover; result.Color.Foreground.PrimaryLightActive = bitTheme.Color.Foreground.PrimaryLightActive ?? other.Color.Foreground.PrimaryLightActive; + result.Color.Foreground.PrimaryDisabled = bitTheme.Color.Foreground.PrimaryDisabled ?? other.Color.Foreground.PrimaryDisabled; + result.Color.Foreground.PrimaryDisabledText = bitTheme.Color.Foreground.PrimaryDisabledText ?? other.Color.Foreground.PrimaryDisabledText; + result.Color.Foreground.PrimaryFocus = bitTheme.Color.Foreground.PrimaryFocus ?? other.Color.Foreground.PrimaryFocus; result.Color.Foreground.Secondary = bitTheme.Color.Foreground.Secondary ?? other.Color.Foreground.Secondary; result.Color.Foreground.SecondaryHover = bitTheme.Color.Foreground.SecondaryHover ?? other.Color.Foreground.SecondaryHover; result.Color.Foreground.SecondaryActive = bitTheme.Color.Foreground.SecondaryActive ?? other.Color.Foreground.SecondaryActive; @@ -472,6 +621,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Foreground.SecondaryLight = bitTheme.Color.Foreground.SecondaryLight ?? other.Color.Foreground.SecondaryLight; result.Color.Foreground.SecondaryLightHover = bitTheme.Color.Foreground.SecondaryLightHover ?? other.Color.Foreground.SecondaryLightHover; result.Color.Foreground.SecondaryLightActive = bitTheme.Color.Foreground.SecondaryLightActive ?? other.Color.Foreground.SecondaryLightActive; + result.Color.Foreground.SecondaryDisabled = bitTheme.Color.Foreground.SecondaryDisabled ?? other.Color.Foreground.SecondaryDisabled; + result.Color.Foreground.SecondaryDisabledText = bitTheme.Color.Foreground.SecondaryDisabledText ?? other.Color.Foreground.SecondaryDisabledText; + result.Color.Foreground.SecondaryFocus = bitTheme.Color.Foreground.SecondaryFocus ?? other.Color.Foreground.SecondaryFocus; result.Color.Foreground.Tertiary = bitTheme.Color.Foreground.Tertiary ?? other.Color.Foreground.Tertiary; result.Color.Foreground.TertiaryHover = bitTheme.Color.Foreground.TertiaryHover ?? other.Color.Foreground.TertiaryHover; result.Color.Foreground.TertiaryActive = bitTheme.Color.Foreground.TertiaryActive ?? other.Color.Foreground.TertiaryActive; @@ -481,6 +633,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Foreground.TertiaryLight = bitTheme.Color.Foreground.TertiaryLight ?? other.Color.Foreground.TertiaryLight; result.Color.Foreground.TertiaryLightHover = bitTheme.Color.Foreground.TertiaryLightHover ?? other.Color.Foreground.TertiaryLightHover; result.Color.Foreground.TertiaryLightActive = bitTheme.Color.Foreground.TertiaryLightActive ?? other.Color.Foreground.TertiaryLightActive; + result.Color.Foreground.TertiaryDisabled = bitTheme.Color.Foreground.TertiaryDisabled ?? other.Color.Foreground.TertiaryDisabled; + result.Color.Foreground.TertiaryDisabledText = bitTheme.Color.Foreground.TertiaryDisabledText ?? other.Color.Foreground.TertiaryDisabledText; + result.Color.Foreground.TertiaryFocus = bitTheme.Color.Foreground.TertiaryFocus ?? other.Color.Foreground.TertiaryFocus; result.Color.Foreground.Disabled = bitTheme.Color.Foreground.Disabled ?? other.Color.Foreground.Disabled; result.Color.Background.Primary = bitTheme.Color.Background.Primary ?? other.Color.Background.Primary; @@ -492,6 +647,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Background.PrimaryLight = bitTheme.Color.Background.PrimaryLight ?? other.Color.Background.PrimaryLight; result.Color.Background.PrimaryLightHover = bitTheme.Color.Background.PrimaryLightHover ?? other.Color.Background.PrimaryLightHover; result.Color.Background.PrimaryLightActive = bitTheme.Color.Background.PrimaryLightActive ?? other.Color.Background.PrimaryLightActive; + result.Color.Background.PrimaryDisabled = bitTheme.Color.Background.PrimaryDisabled ?? other.Color.Background.PrimaryDisabled; + result.Color.Background.PrimaryDisabledText = bitTheme.Color.Background.PrimaryDisabledText ?? other.Color.Background.PrimaryDisabledText; + result.Color.Background.PrimaryFocus = bitTheme.Color.Background.PrimaryFocus ?? other.Color.Background.PrimaryFocus; result.Color.Background.Secondary = bitTheme.Color.Background.Secondary ?? other.Color.Background.Secondary; result.Color.Background.SecondaryHover = bitTheme.Color.Background.SecondaryHover ?? other.Color.Background.SecondaryHover; result.Color.Background.SecondaryActive = bitTheme.Color.Background.SecondaryActive ?? other.Color.Background.SecondaryActive; @@ -501,6 +659,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Background.SecondaryLight = bitTheme.Color.Background.SecondaryLight ?? other.Color.Background.SecondaryLight; result.Color.Background.SecondaryLightHover = bitTheme.Color.Background.SecondaryLightHover ?? other.Color.Background.SecondaryLightHover; result.Color.Background.SecondaryLightActive = bitTheme.Color.Background.SecondaryLightActive ?? other.Color.Background.SecondaryLightActive; + result.Color.Background.SecondaryDisabled = bitTheme.Color.Background.SecondaryDisabled ?? other.Color.Background.SecondaryDisabled; + result.Color.Background.SecondaryDisabledText = bitTheme.Color.Background.SecondaryDisabledText ?? other.Color.Background.SecondaryDisabledText; + result.Color.Background.SecondaryFocus = bitTheme.Color.Background.SecondaryFocus ?? other.Color.Background.SecondaryFocus; result.Color.Background.Tertiary = bitTheme.Color.Background.Tertiary ?? other.Color.Background.Tertiary; result.Color.Background.TertiaryHover = bitTheme.Color.Background.TertiaryHover ?? other.Color.Background.TertiaryHover; result.Color.Background.TertiaryActive = bitTheme.Color.Background.TertiaryActive ?? other.Color.Background.TertiaryActive; @@ -510,6 +671,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Background.TertiaryLight = bitTheme.Color.Background.TertiaryLight ?? other.Color.Background.TertiaryLight; result.Color.Background.TertiaryLightHover = bitTheme.Color.Background.TertiaryLightHover ?? other.Color.Background.TertiaryLightHover; result.Color.Background.TertiaryLightActive = bitTheme.Color.Background.TertiaryLightActive ?? other.Color.Background.TertiaryLightActive; + result.Color.Background.TertiaryDisabled = bitTheme.Color.Background.TertiaryDisabled ?? other.Color.Background.TertiaryDisabled; + result.Color.Background.TertiaryDisabledText = bitTheme.Color.Background.TertiaryDisabledText ?? other.Color.Background.TertiaryDisabledText; + result.Color.Background.TertiaryFocus = bitTheme.Color.Background.TertiaryFocus ?? other.Color.Background.TertiaryFocus; result.Color.Background.Disabled = bitTheme.Color.Background.Disabled ?? other.Color.Background.Disabled; result.Color.Background.Overlay = bitTheme.Color.Background.Overlay ?? other.Color.Background.Overlay; @@ -522,6 +686,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Border.PrimaryLight = bitTheme.Color.Border.PrimaryLight ?? other.Color.Border.PrimaryLight; result.Color.Border.PrimaryLightHover = bitTheme.Color.Border.PrimaryLightHover ?? other.Color.Border.PrimaryLightHover; result.Color.Border.PrimaryLightActive = bitTheme.Color.Border.PrimaryLightActive ?? other.Color.Border.PrimaryLightActive; + result.Color.Border.PrimaryDisabled = bitTheme.Color.Border.PrimaryDisabled ?? other.Color.Border.PrimaryDisabled; + result.Color.Border.PrimaryDisabledText = bitTheme.Color.Border.PrimaryDisabledText ?? other.Color.Border.PrimaryDisabledText; + result.Color.Border.PrimaryFocus = bitTheme.Color.Border.PrimaryFocus ?? other.Color.Border.PrimaryFocus; result.Color.Border.Secondary = bitTheme.Color.Border.Secondary ?? other.Color.Border.Secondary; result.Color.Border.SecondaryHover = bitTheme.Color.Border.SecondaryHover ?? other.Color.Border.SecondaryHover; result.Color.Border.SecondaryActive = bitTheme.Color.Border.SecondaryActive ?? other.Color.Border.SecondaryActive; @@ -531,6 +698,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Border.SecondaryLight = bitTheme.Color.Border.SecondaryLight ?? other.Color.Border.SecondaryLight; result.Color.Border.SecondaryLightHover = bitTheme.Color.Border.SecondaryLightHover ?? other.Color.Border.SecondaryLightHover; result.Color.Border.SecondaryLightActive = bitTheme.Color.Border.SecondaryLightActive ?? other.Color.Border.SecondaryLightActive; + result.Color.Border.SecondaryDisabled = bitTheme.Color.Border.SecondaryDisabled ?? other.Color.Border.SecondaryDisabled; + result.Color.Border.SecondaryDisabledText = bitTheme.Color.Border.SecondaryDisabledText ?? other.Color.Border.SecondaryDisabledText; + result.Color.Border.SecondaryFocus = bitTheme.Color.Border.SecondaryFocus ?? other.Color.Border.SecondaryFocus; result.Color.Border.Tertiary = bitTheme.Color.Border.Tertiary ?? other.Color.Border.Tertiary; result.Color.Border.TertiaryHover = bitTheme.Color.Border.TertiaryHover ?? other.Color.Border.TertiaryHover; result.Color.Border.TertiaryActive = bitTheme.Color.Border.TertiaryActive ?? other.Color.Border.TertiaryActive; @@ -540,6 +710,9 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Color.Border.TertiaryLight = bitTheme.Color.Border.TertiaryLight ?? other.Color.Border.TertiaryLight; result.Color.Border.TertiaryLightHover = bitTheme.Color.Border.TertiaryLightHover ?? other.Color.Border.TertiaryLightHover; result.Color.Border.TertiaryLightActive = bitTheme.Color.Border.TertiaryLightActive ?? other.Color.Border.TertiaryLightActive; + result.Color.Border.TertiaryDisabled = bitTheme.Color.Border.TertiaryDisabled ?? other.Color.Border.TertiaryDisabled; + result.Color.Border.TertiaryDisabledText = bitTheme.Color.Border.TertiaryDisabledText ?? other.Color.Border.TertiaryDisabledText; + result.Color.Border.TertiaryFocus = bitTheme.Color.Border.TertiaryFocus ?? other.Color.Border.TertiaryFocus; result.Color.Border.Disabled = bitTheme.Color.Border.Disabled ?? other.Color.Border.Disabled; result.Color.Required = bitTheme.Color.Required ?? other.Color.Required; @@ -602,6 +775,7 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.BoxShadow.S22 = bitTheme.BoxShadow.S22 ?? other.BoxShadow.S22; result.BoxShadow.S23 = bitTheme.BoxShadow.S23 ?? other.BoxShadow.S23; result.BoxShadow.S24 = bitTheme.BoxShadow.S24 ?? other.BoxShadow.S24; + result.BoxShadow.FocusRing = bitTheme.BoxShadow.FocusRing ?? other.BoxShadow.FocusRing; result.Spacing.ScalingFactor = bitTheme.Spacing.ScalingFactor ?? other.Spacing.ScalingFactor; @@ -614,6 +788,8 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Shape.BorderRadius = bitTheme.Shape.BorderRadius ?? other.Shape.BorderRadius; result.Shape.BorderWidth = bitTheme.Shape.BorderWidth ?? other.Shape.BorderWidth; result.Shape.BorderStyle = bitTheme.Shape.BorderStyle ?? other.Shape.BorderStyle; + result.Shape.FocusRingWidth = bitTheme.Shape.FocusRingWidth ?? other.Shape.FocusRingWidth; + result.Shape.FocusRingOffset = bitTheme.Shape.FocusRingOffset ?? other.Shape.FocusRingOffset; result.Typography.FontFamily = bitTheme.Typography.FontFamily ?? other.Typography.FontFamily; result.Typography.FontWeight = bitTheme.Typography.FontWeight ?? other.Typography.FontWeight; @@ -715,6 +891,20 @@ internal static BitTheme Merge(BitTheme bitTheme, BitTheme other) result.Typography.Subtitle2.LineHeight = bitTheme.Typography.Subtitle2.LineHeight ?? other.Typography.Subtitle2.LineHeight; result.Typography.Subtitle2.LetterSpacing = bitTheme.Typography.Subtitle2.LetterSpacing ?? other.Typography.Subtitle2.LetterSpacing; + result.Motion.Duration = bitTheme.Motion.Duration ?? other.Motion.Duration; + result.Motion.DurationShort = bitTheme.Motion.DurationShort ?? other.Motion.DurationShort; + result.Motion.DurationLong = bitTheme.Motion.DurationLong ?? other.Motion.DurationLong; + result.Motion.EasingStandard = bitTheme.Motion.EasingStandard ?? other.Motion.EasingStandard; + + result.Layout.Direction = bitTheme.Layout.Direction ?? other.Layout.Direction; + result.Layout.DensityScale = bitTheme.Layout.DensityScale ?? other.Layout.DensityScale; + result.Layout.Breakpoints.Xs = bitTheme.Layout.Breakpoints.Xs ?? other.Layout.Breakpoints.Xs; + result.Layout.Breakpoints.Sm = bitTheme.Layout.Breakpoints.Sm ?? other.Layout.Breakpoints.Sm; + result.Layout.Breakpoints.Md = bitTheme.Layout.Breakpoints.Md ?? other.Layout.Breakpoints.Md; + result.Layout.Breakpoints.Lg = bitTheme.Layout.Breakpoints.Lg ?? other.Layout.Breakpoints.Lg; + result.Layout.Breakpoints.Xl = bitTheme.Layout.Breakpoints.Xl ?? other.Layout.Breakpoints.Xl; + result.Layout.Breakpoints.Xxl = bitTheme.Layout.Breakpoints.Xxl ?? other.Layout.Breakpoints.Xxl; + return result; } } diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeName.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeName.cs new file mode 100644 index 0000000000..c4b0e730da --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeName.cs @@ -0,0 +1,72 @@ +namespace Bit.BlazorUI; + +/// +/// Type-safe wrapper for the bit-theme attribute name. Use the static factories +/// (, , , etc.) for the well-known presets; +/// call for app-specific theme names that match your CSS. +/// +/// +/// +/// Existing callers can keep using the string constants on ; +/// this struct is additive and provides an implicit conversion to so it slots +/// straight into without ceremony. +/// +/// +/// The wrapper normalizes the value (trim + lower-case invariant) so Custom("Fluent-Light") +/// and BitThemeName.FluentLight compare equal. That mirrors the JS side, which writes the +/// value verbatim onto document.documentElement and lets CSS selectors (:root[bit-theme="…"]) +/// match in a case-sensitive fashion against the canonical lowercase form. +/// +/// +public readonly struct BitThemeName : IEquatable +{ + private readonly string? _value; + + private BitThemeName(string value) + { + _value = value; + } + + /// The normalized theme name (trimmed, lower-case invariant). + public string Value => _value ?? BitThemePresets.Light; + + /// Light preset ("light"). + public static BitThemeName Light { get; } = new(BitThemePresets.Light); + + /// Dark preset ("dark"). + public static BitThemeName Dark { get; } = new(BitThemePresets.Dark); + + /// Fluent base preset ("fluent"). + public static BitThemeName Fluent { get; } = new(BitThemePresets.Fluent); + + /// Fluent light preset ("fluent-light"). + public static BitThemeName FluentLight { get; } = new(BitThemePresets.FluentLight); + + /// Fluent dark preset ("fluent-dark"). + public static BitThemeName FluentDark { get; } = new(BitThemePresets.FluentDark); + + /// Special pseudo-preset that follows OS prefers-color-scheme ("system"). + public static BitThemeName System { get; } = new(BitThemePresets.System); + + /// Wraps a custom theme name. Whitespace is trimmed and the result is lower-cased. + /// When is null, empty, or whitespace. + public static BitThemeName Custom(string name) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + return new BitThemeName(name.Trim().ToLowerInvariant()); + } + + /// Implicitly converts to the underlying string so existing string-typed APIs accept the wrapper unchanged. + public static implicit operator string(BitThemeName name) => name.Value; + + public override string ToString() => Value; + + public bool Equals(BitThemeName other) => string.Equals(Value, other.Value, StringComparison.Ordinal); + + public override bool Equals(object? obj) => obj is BitThemeName other && Equals(other); + + public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(Value); + + public static bool operator ==(BitThemeName left, BitThemeName right) => left.Equals(right); + public static bool operator !=(BitThemeName left, BitThemeName right) => !left.Equals(right); +} diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeNotifications.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeNotifications.cs new file mode 100644 index 0000000000..373aaafb5e --- /dev/null +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeNotifications.cs @@ -0,0 +1,64 @@ +using Microsoft.Extensions.Logging; + +namespace Bit.BlazorUI; + +/// +/// Raised when the global bit-theme document attribute changes (including OS-driven updates when following system theme). +/// Subscribe in scoped components; requires interop at least once per circuit so the client script can notify .NET. +/// +/// +/// +/// Subscribers MUST unsubscribe when their component is disposed. +/// is registered as a scoped service, so a leaked handler keeps the (potentially disposed) component +/// rooted for the lifetime of the circuit and produces re-renders on torn-down state. +/// +/// +/// Handlers are invoked on whichever thread is called from (typically the JS +/// interop callback thread). If you need to update Blazor component state, marshal back through +/// ComponentBase.InvokeAsync(StateHasChanged). +/// +/// +/// A throwing handler does not prevent the remaining handlers from running and is logged when an +/// is registered in DI. +/// +/// +public sealed class BitThemeNotifications +{ + private readonly ILogger? _logger; + + public BitThemeNotifications() + { + } + + public BitThemeNotifications(ILoggerFactory? loggerFactory) + { + _logger = loggerFactory?.CreateLogger(); + } + + /// Fires after BitBlazorUI.Theme.set, toggleDarkLight, or prefers-color-scheme updates while following system theme. + public event EventHandler? ThemeChanged; + + internal void Raise(string? newTheme, string? oldTheme) + { + var handler = ThemeChanged; + if (handler is null) return; + + var args = new BitThemeChangedEventArgs(newTheme, oldTheme); + + // Invoke each subscriber in isolation. EventHandler.Invoke would short-circuit the + // remaining delegates on the first throw, and any escaping exception from the JS-invoked + // path can fault the circuit. Per-subscriber try/catch + log is the correct contract for + // a notification service consumed by arbitrary user code. + foreach (var subscriber in handler.GetInvocationList()) + { + try + { + ((EventHandler)subscriber).Invoke(this, args); + } + catch (Exception ex) + { + _logger?.LogError(ex, "BitThemeNotifications.ThemeChanged handler threw and was suppressed."); + } + } + } +} diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeProvider.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeProvider.cs index 9ccecda73e..d1cd846ef5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeProvider.cs +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeProvider.cs @@ -1,6 +1,6 @@ namespace Bit.BlazorUI; -public partial class BitThemeProvider : ComponentBase +public sealed class BitThemeProvider : ComponentBase { /// /// The content of the ThemeProvider. @@ -8,8 +8,15 @@ public partial class BitThemeProvider : ComponentBase [Parameter] public RenderFragment? ChildContent { get; set; } /// - /// The element used for the root node. + /// The element used for the root node. Ignored when no wrapping element is needed + /// (i.e. when both and are ). /// + /// + /// When a wrapping element is rendered it defaults to display:contents so it produces no + /// layout box and stays transparent to the surrounding flex/grid layout, while still letting the + /// applied CSS custom properties inherit to descendants. Supply style="display:…" via the + /// splatted attributes to opt back into a real box (e.g. when you also set box-model styles on it). + /// [Parameter] public string? RootElement { get; set; } /// @@ -19,53 +26,166 @@ public partial class BitThemeProvider : ComponentBase /// /// Optional name for ; when set, consumers use [CascadingParameter(Name = …)]. - /// The cascaded is the merge of with (same as inline CSS variables on this provider’s root). + /// The cascaded is the merge of with (same as inline CSS variables on this provider's root). /// [Parameter] public string? ThemeName { get; set; } + /// + /// Catch-all for HTML attributes splatted onto the wrapping element (e.g. class, id, + /// data-*, ARIA roles). Only emitted when this provider renders a wrapping element — that + /// is, when a local is applied (its CSS variables need a host element), or + /// when a cascading is re-exposed under a custom . + /// When there is nothing to apply at this layer (no local and no + /// ) the provider renders just and these + /// attributes are ignored. + /// + [Parameter(CaptureUnmatchedValues = true)] + public IReadOnlyDictionary? AdditionalAttributes { get; set; } - [CascadingParameter] public BitTheme? ParentTheme { get; set; } + [CascadingParameter] public BitTheme? ParentTheme { get; set; } - protected override void BuildRenderTree(RenderTreeBuilder builder) + // Cached merge result and the inline `style` string. + // + // BitTheme is mutable (every node is a class with public setters), so callers can keep a + // single Theme/ParentTheme instance and mutate sub-properties between renders. We therefore + // rebuild the merge + CSS string on every parameters update and use CSS-string equality as a + // cheap structural check: when the rebuilt output matches the cached one we keep the cached + // merged-theme reference, which keeps CascadingValue reference-stable so + // descendants don't re-render on parent re-renders that didn't actually change tokens. + // (CascadingValue for reference types uses ReferenceEquals for change detection, and + // BitThemeMapper.Merge produces a fresh BitTheme on every call.) + private BitTheme? _cachedMergedTheme; + private string? _cachedCssVarStyle; + + protected override void OnParametersSet() { - var seq = 0; + if (Theme is null && ParentTheme is null) + { + // No tokens to apply at this layer; we'll render ChildContent directly. + _cachedMergedTheme = null; + _cachedCssVarStyle = null; + return; + } if (Theme is null) { - builder.AddContent(seq, ChildContent); + // Local theme not set, but a ParentTheme is cascading from above. Keep it as the value + // we would re-expose, and emit no inline style at this layer (the ancestor provider owns + // those CSS vars). BuildRenderTree only renders a wrapper + CascadingValue when a custom + // ThemeName needs the value re-exposed under that name; otherwise it renders ChildContent + // directly and relies on the ancestor's existing unnamed cascade, avoiding a redundant + // wrapper element and duplicate CascadingValue. + _cachedMergedTheme = ParentTheme; + _cachedCssVarStyle = null; return; } - var mergedTheme = Theme!; + // Always produce a FRESH merged BitTheme (BitThemeMapper.Merge allocates a new instance), + // even when there is no ParentTheme. This keeps the CascadingValue change-detection + // behavior consistent: previously the no-parent path cascaded the caller's own Theme instance, + // so an in-place mutation of that instance produced the same reference and CascadingValue + // (which compares reference types with ReferenceEquals) never notified [CascadingParameter] + // consumers — whereas the with-parent path always allocated a new reference and did notify. + // Going through Merge in both cases means a token change yields a new reference (consumers are + // notified), while the CSS-string equality suppression below preserves the cached reference + // when nothing actually changed (consumers are not woken up needlessly). + var mergedTheme = BitThemeMapper.Merge(Theme, ParentTheme ?? new BitTheme()); + + var cssVarStyle = BuildCssVarStyle(mergedTheme); + + // Suppress propagation when the produced output is identical to the previous render — + // this preserves the cascaded reference and avoids waking up every descendant consumer + // when a parent re-renders without touching the theme. + if (_cachedMergedTheme is not null && string.Equals(_cachedCssVarStyle, cssVarStyle, StringComparison.Ordinal)) + { + return; + } + + _cachedMergedTheme = mergedTheme; + _cachedCssVarStyle = cssVarStyle; + } + + protected override void BuildRenderTree(RenderTreeBuilder builder) + { + if (_cachedMergedTheme is null) + { + // No local Theme override and no parent theme to re-cascade: render ChildContent as-is. + builder.AddContent(0, ChildContent); + return; + } - if (ParentTheme is not null) + if (_cachedCssVarStyle is null && ThemeName is null) { - mergedTheme = BitThemeMapper.Merge(Theme, ParentTheme); + // Nothing to apply at this layer: no CSS variables to emit (Theme is null — only a + // ParentTheme is cascading from above) and no custom ThemeName to re-expose the value + // under. The ancestor's unnamed CascadingValue already reaches our + // descendants, so rendering ChildContent directly preserves the cascade while avoiding + // a redundant wrapper element and a duplicate CascadingValue. + builder.AddContent(0, ChildContent); + return; } - var cssVars = BitThemeMapper.MapToCssVariables(mergedTheme); - var cssVarStyle = string.Join(';', cssVars.Select(kv => $"{kv.Key}:{kv.Value}")); + builder.OpenElement(1, RootElement ?? "div"); - builder.OpenElement(seq++, RootElement ?? "div"); - builder.AddAttribute(seq++, "style", cssVarStyle); + // Splat user attributes individually so we can pull out any "style" entry and merge it + // with our CSS-variable declarations (last-write-wins semantics on RenderTreeBuilder mean + // a single AddMultipleAttributes followed by AddAttribute("style", ...) would silently + // drop the user's style). + string? userStyle = null; + if (AdditionalAttributes is not null) + { + foreach (var kv in AdditionalAttributes) + { + if (string.Equals(kv.Key, "style", StringComparison.OrdinalIgnoreCase)) + { + userStyle = kv.Value?.ToString(); + continue; + } + + builder.AddAttribute(2, kv.Key, kv.Value); + } + } - builder.OpenComponent>(seq++); - if (ThemeName is not null) + // Compose the inline style. The wrapper defaults to `display:contents` so it produces no + // layout box of its own: the element must exist for CSS custom properties to inherit to + // descendants (inheritance follows the DOM tree, not the box tree), but it should not + // perturb the surrounding flex/grid layout or child selectors the way a default `div` + // block box would. The CSS-variable declarations come next, and the user-supplied style + // comes last so a caller can override both the layout mode (e.g. `style="display:flex"`) + // and any individual token on conflict. + var style = "display:contents"; + if (_cachedCssVarStyle is not null) { - builder.AddAttribute(seq++, "Name", ThemeName); - builder.AddAttribute(seq++, "Value", mergedTheme); + style = $"{style};{_cachedCssVarStyle}"; } - else + if (userStyle is not null) { - builder.AddAttribute(seq++, "Value", mergedTheme); + style = $"{style};{userStyle}"; } - builder.AddAttribute(seq++, "ChildContent", (RenderFragment)(builder2 => builder2.AddContent(seq, ChildContent))); + + builder.AddAttribute(3, "style", style); + + builder.OpenComponent>(4); + if (ThemeName is not null) + { + builder.AddAttribute(5, "Name", ThemeName); + } + builder.AddAttribute(6, "Value", _cachedMergedTheme); + // IMPORTANT: do NOT close over a mutable outer sequence counter. The lambda runs lazily + // during the cascade's render pass, so capturing a mutable local would feed Blazor a + // sequence number that varies per render and defeats its diff. Sequence numbers inside a + // RenderFragment are local to that fragment, so a constant is what we want. + builder.AddAttribute(7, "ChildContent", (RenderFragment)(b => b.AddContent(0, ChildContent))); builder.CloseComponent(); builder.CloseElement(); + } - base.BuildRenderTree(builder); + private static string BuildCssVarStyle(BitTheme theme) + { + var cssVars = BitThemeUtilities.ToCssVariables(theme); + return string.Join(';', cssVars.Select(kv => $"{kv.Key}:{kv.Value}")); } } diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSerialization.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSerialization.cs index cfa79c9d17..dad2f3d695 100644 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSerialization.cs +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSerialization.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Reflection; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -61,36 +59,75 @@ private static void PruneEmptyObjectNodes(JsonNode? node) } } - /// Replaces null reference-type properties (except ) so the tree matches a new BitTheme() graph after sparse JSON. -#pragma warning disable IL2072 // Activator.CreateInstance — BitTheme-related POCOs only, all have public parameterless constructors. -#pragma warning disable IL2075 // GetType().GetProperties — only instances from BitTheme deserialization. - private static void EnsureNestedObjects(object obj, HashSet? visited = null) + /// + /// Replaces branch objects on a deserialized so the + /// graph matches a freshly-constructed new BitTheme(). Callers that walk the graph (the + /// mapper, merge, derivation helpers) rely on every branch being non-null. + /// + /// + /// + /// This is a safety net for externally-authored JSON. The sparse format produced by + /// never emits a branch as explicit null (null leaves are omitted + /// and empty objects are pruned), and leaves absent + /// properties at their constructor-initialized new() values — so a framework round-trip + /// already yields a fully-populated graph. The case this guards is hand-written or third-party + /// JSON that contains an explicit "color": null (or similar), which the serializer would + /// otherwise honor by setting the branch to . + /// + /// + /// Previously this method walked the type via reflection. Reflection breaks under trimming / + /// AOT (IL2070/IL2075/IL3050) unless the entire graph is preserved by + /// [DynamicallyAccessedMembers], which is hard to keep correct as the model evolves. + /// Walking the graph explicitly is verbose but trim-safe and removes the reflection + /// suppression pragmas that previously hid genuine warnings. + /// + /// + private static void EnsureNestedObjects(BitTheme theme) { - visited ??= new HashSet(ReferenceEqualityComparer.Instance); - if (!visited.Add(obj)) - return; + // Top-level branches. + theme.Color ??= new BitThemeColors(); + theme.BoxShadow ??= new BitThemeBoxShadows(); + theme.Spacing ??= new BitThemeSpacings(); + theme.ZIndex ??= new BitThemeZIndices(); + theme.Shape ??= new BitThemeShapes(); + theme.Typography ??= new BitThemeTypography(); + theme.Motion ??= new BitThemeMotion(); + theme.Layout ??= new BitThemeLayout(); - foreach (var prop in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - if (!prop.CanRead || !prop.CanWrite || prop.GetIndexParameters().Length > 0) - continue; + // Color branch. + var color = theme.Color; + color.Primary ??= new BitThemeColorVariants(); + color.Secondary ??= new BitThemeColorVariants(); + color.Tertiary ??= new BitThemeColorVariants(); + color.Info ??= new BitThemeColorVariants(); + color.Success ??= new BitThemeColorVariants(); + color.Warning ??= new BitThemeColorVariants(); + color.SevereWarning ??= new BitThemeColorVariants(); + color.Error ??= new BitThemeColorVariants(); + color.Foreground ??= new BitThemeGeneralColorVariants(); + color.Background ??= new BitThemeBackgroundColorVariants(); + color.Border ??= new BitThemeGeneralColorVariants(); + color.Neutral ??= new BitThemeNeutralColorVariants(); - var pt = prop.PropertyType; - if (pt == typeof(string) || pt.IsValueType) - continue; + // Typography branch. + var typography = theme.Typography; + typography.H1 ??= new BitThemeTypographyVariants(); + typography.H2 ??= new BitThemeTypographyVariants(); + typography.H3 ??= new BitThemeTypographyVariants(); + typography.H4 ??= new BitThemeTypographyVariants(); + typography.H5 ??= new BitThemeTypographyVariants(); + typography.H6 ??= new BitThemeTypographyVariants(); + typography.Subtitle1 ??= new BitThemeTypographyVariants(); + typography.Subtitle2 ??= new BitThemeTypographyVariants(); + typography.Body1 ??= new BitThemeTypographyVariants(); + typography.Body2 ??= new BitThemeTypographyVariants(); + typography.Button ??= new BitThemeLabelTypographyVariants(); + typography.Caption1 ??= new BitThemeTypographyVariants(); + typography.Caption2 ??= new BitThemeTypographyVariants(); + typography.Overline ??= new BitThemeLabelTypographyVariants(); + typography.Inherit ??= new BitThemeInheritTypographyVariants(); - var val = prop.GetValue(obj); - if (val is null) - { - val = Activator.CreateInstance(pt); - if (val is null) - continue; - prop.SetValue(obj, val); - } - - EnsureNestedObjects(val, visited); - } + // Layout branch. + theme.Layout.Breakpoints ??= new BitThemeBreakpoints(); } -#pragma warning restore IL2075 -#pragma warning restore IL2072 } diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSsr.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSsr.cs index e50663f56c..f60f4e3140 100644 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSsr.cs +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeSsr.cs @@ -5,20 +5,182 @@ namespace Bit.BlazorUI; /// Order matches bit-theme.ts init: base from attributes, then bit-theme-system (prefers-color-scheme), then bit-theme-persist (localStorage), then resolve stored system. /// Emit at the start of <head> (before stylesheets) so the correct bit-theme attribute is set before first paint. /// +/// +/// Attribute and storage key names come from so the inline script and the runtime BitBlazorUI.Theme client stay in sync. +/// public static class BitThemeSsr { /// - /// Inline script only (no script tag). Wrap in a script element in your host page or layout. + /// Inline script body without a wrapping <script> tag. Concatenate into your own + /// <script> element when you need full control over attributes (CSP nonce, + /// type, defer, etc.). /// - public const string InlineHeadScriptBody = - "(function(){var r=document.documentElement,k='bit-current-theme',lt=r.getAttribute('bit-theme-light')||'light'," + - "dk=r.getAttribute('bit-theme-dark')||'dark',m=window.matchMedia&&matchMedia('(prefers-color-scheme:dark)').matches," + - "base=r.getAttribute('bit-theme')||r.getAttribute('bit-theme-default')||'light';" + - "if(r.hasAttribute('bit-theme-system')){base=m?dk:lt;}" + - "var cur=base;if(r.hasAttribute('bit-theme-persist')){try{cur=localStorage.getItem(k)||base;}catch(e){}}" + - "if(cur==='system'){cur=m?dk:lt;}" + - "r.setAttribute('bit-theme',cur);})();"; - - /// Full script element markup for convenience. - public static string InlineHeadScript => $""; + public static readonly string InlineHeadScriptBody = BuildInlineScriptBody(); + + /// + /// Full <script> markup ready to drop into <head>. Equivalent to + /// with a nonce. + /// + public static string InlineHeadScript => BuildInlineHeadScript(nonce: null); + + /// + /// Builds the inline first-paint script and wraps it in a <script> element. + /// Pass to satisfy a script-src 'nonce-…' Content-Security-Policy. + /// + /// Optional CSP nonce. When supplied, the value is HTML-attribute-encoded and emitted as nonce="…". + public static string BuildInlineHeadScript(string? nonce) + { + if (string.IsNullOrWhiteSpace(nonce)) + { + return $""; + } + + // HTML-attribute-encode the nonce so a tampered value cannot break out of the attribute. + // CSP nonces are base64url in practice, but defense-in-depth is cheap. + var safeNonce = HtmlEncodeAttribute(nonce); + return $""; + } + + /// + /// Builds the bit-theme* attribute string to render on the root <html> + /// element from a persisted preference (typically read from the + /// cookie) so server-rendered markup paints + /// the correct theme before the client script runs. + /// + /// + /// The stored abstract theme key (for example dark, fluent-light, or + /// system). Usually the cookie value; may be /blank when no + /// preference has been stored yet. + /// + /// + /// Optional theme to use when is missing, or as the + /// no-JS base when the OS is being followed. + /// + /// + /// An attribute fragment such as bit-theme="dark" or + /// bit-theme-system bit-theme-default="light", ready to interpolate into the root + /// <html> tag. + /// + /// + /// + /// A concrete preference is emitted as bit-theme="…" so first paint is correct with no + /// flash. The special system value (and a missing preference with no default) emit the + /// bit-theme-system marker and defer the light/dark resolution to the client inline + /// script — the server cannot read prefers-color-scheme. A missing preference with a + /// concrete paints that default directly. + /// + /// + /// Values are normalized (trim + lower-case, matching ) and validated + /// against a strict [a-z0-9-] theme-name pattern; anything else is ignored and treated as + /// missing, so a tampered cookie cannot inject markup into the document. + /// + /// + /// This is the server half of cookie-based persistence: the app is responsible for writing the + /// cookie (client- or server-side) under . + /// + /// + public static string BuildRootThemeAttributes(string? persistedPreference, string? defaultTheme = null) + { + var preference = NormalizeThemeToken(persistedPreference); + var fallback = NormalizeThemeToken(defaultTheme); + + // Explicit "system" → follow the OS; the inline head script resolves light/dark before + // paint. Carry a concrete default through for no-JS clients. + if (preference == BitThemePresets.System) + { + return fallback is null || fallback == BitThemePresets.System + ? BitThemeAttributeNames.ThemeSystem + : $"{BitThemeAttributeNames.ThemeSystem} {BitThemeAttributeNames.ThemeDefault}=\"{fallback}\""; + } + + // Concrete persisted preference → paint it directly (no flash). + if (preference is not null) + { + return $"{BitThemeAttributeNames.Theme}=\"{preference}\""; + } + + // No stored preference: use the configured default theme, or follow the OS if none. + if (fallback is not null && fallback != BitThemePresets.System) + { + return $"{BitThemeAttributeNames.Theme}=\"{fallback}\""; + } + + return BitThemeAttributeNames.ThemeSystem; + } + + /// + /// Normalizes a theme key (trim + lower-case invariant) and validates it as a safe + /// [a-z0-9-] token. Returns for blank or invalid input so callers + /// treat it as "no preference" — this also prevents a tampered cookie value from being emitted + /// into the document. + /// + private static string? NormalizeThemeToken(string? value) + { + if (string.IsNullOrWhiteSpace(value)) return null; + + var token = value.Trim().ToLowerInvariant(); + if (token.Length > 64) return null; + + foreach (var ch in token) + { + var allowed = (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-'; + if (allowed is false) return null; + } + + return token; + } + + private static string BuildInlineScriptBody() + { + // Centralizing names through BitThemeAttributeNames means the inline script picks up renames + // automatically — the previous version hard-coded each attribute literal twice (here and in + // bit-theme.ts), which was a maintenance hazard. + var theme = BitThemeAttributeNames.Theme; + var themeDefault = BitThemeAttributeNames.ThemeDefault; + var themeSystem = BitThemeAttributeNames.ThemeSystem; + var themePersist = BitThemeAttributeNames.ThemePersist; + var themePersistCookie = BitThemeAttributeNames.ThemePersistCookie; + var themeDark = BitThemeAttributeNames.ThemeDark; + var themeLight = BitThemeAttributeNames.ThemeLight; + var storageKey = BitThemeAttributeNames.ThemeStorageKey; + var cookieName = BitThemeCookie.PreferenceCookieName; + + // The inline script is intentionally compact; it runs on every first paint before stylesheets. + // Logic mirrors Theme.init in bit-theme.ts, including the localStorage→cookie persistence + // fallback so a cookie-only (SSR) persistence setup paints correctly with no flash. + return + "(function(){var r=document.documentElement," + + $"k='{storageKey}'," + + $"ck='{cookieName}'," + + $"lt=r.getAttribute('{themeLight}')||'light'," + + $"dk=r.getAttribute('{themeDark}')||'dark'," + + "m=window.matchMedia&&matchMedia('(prefers-color-scheme:dark)').matches," + + $"base=r.getAttribute('{theme}')||r.getAttribute('{themeDefault}')||'light';" + + $"if(r.hasAttribute('{themeSystem}')){{base=m?dk:lt;}}" + + "var cur=base,got=false;" + + $"if(r.hasAttribute('{themePersist}')){{try{{var s=localStorage.getItem(k);if(s){{cur=s;got=true;}}}}catch(e){{}}}}" + + $"if(!got&&r.hasAttribute('{themePersistCookie}')){{var cm=('; '+document.cookie).match('; '+ck+'=([^;]*)');if(cm){{try{{cur=decodeURIComponent(cm[1]);}}catch(e){{cur=cm[1];}}}}}}" + + "if(cur==='system'){cur=m?dk:lt;}" + + $"r.setAttribute('{theme}',cur);}})();"; + } + + private static string HtmlEncodeAttribute(string value) + { + // Minimal attribute-context encoding; we don't want a full WebUtility dep just for this and + // CSP nonces are constrained to base64url chars in practice. Encode the characters that can + // break out of a double-quoted attribute or alter parsing. + var builder = new System.Text.StringBuilder(value.Length); + foreach (var ch in value) + { + switch (ch) + { + case '&': builder.Append("&"); break; + case '"': builder.Append("""); break; + case '<': builder.Append("<"); break; + case '>': builder.Append(">"); break; + default: builder.Append(ch); break; + } + } + return builder.ToString(); + } } diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeUtilities.cs b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeUtilities.cs index 66e6827ede..4c4e9cf7b5 100644 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeUtilities.cs +++ b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/BitThemeUtilities.cs @@ -5,7 +5,26 @@ /// public static class BitThemeUtilities { - /// Maps a theme to CSS custom property names and values for use with or inline styles. + /// + /// Maps a theme to CSS custom property names and values for use with or inline styles. + /// + /// + /// The result is recomputed on every call. is mutable, so caching by + /// instance would return stale values when callers mutate a theme between calls. The mapper + /// builds a ~280-entry dictionary of strings; for the typical call path through + /// this cost is dwarfed by the JS-interop + /// boundary. If you have a hot, allocation-sensitive path, hold the result yourself and pass + /// the same reference until you intentionally rebuild it. + /// + /// Token values that contain characters capable of escaping a single CSS declaration (for + /// example ;, {, }, <, >, or comment markers) are + /// dropped rather than emitted. Theme values can originate from untrusted sources (see + /// ), and the output is concatenated into an inline + /// style attribute by ; dropping injection-prone values + /// prevents a malicious token from adding extra CSS. A dropped token falls back to the + /// stylesheet default. + /// + /// public static IReadOnlyDictionary ToCssVariables(BitTheme? bitTheme) { return BitThemeMapper.MapToCssVariables(bitTheme ?? new BitTheme()); diff --git a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/bit-theme.ts b/src/BlazorUI/Bit.BlazorUI/Utils/Theme/bit-theme.ts deleted file mode 100644 index 2040d035a2..0000000000 --- a/src/BlazorUI/Bit.BlazorUI/Utils/Theme/bit-theme.ts +++ /dev/null @@ -1,134 +0,0 @@ -type onThemeChangeType = (newThemeName: string, oldThemeName: string) => void; - -interface BitThemeOptions { - system?: boolean; - persist?: boolean; - theme?: string | null; - default?: string | null; - darkTheme?: string | null; - lightTheme?: string | null; - onChange?: onThemeChangeType; -} - -class BitTheme { - private static SYSTEM_THEME = 'system'; - private static THEME_ATTRIBUTE = 'bit-theme'; - private static THEME_STORAGE_KEY = 'bit-current-theme'; - - - private static _persist = false; - private static _darkTheme: string = 'dark'; - private static _lightTheme: string = 'light'; - private static _initOptions: BitThemeOptions = {}; - private static _currentTheme = BitTheme._lightTheme; - private static _onThemeChange: onThemeChangeType = () => { }; - - - public static init(options: BitThemeOptions) { - Object.assign(BitTheme._initOptions, options); - - if (BitTheme._initOptions.onChange) { - BitTheme._onThemeChange = BitTheme._initOptions.onChange; - } - - if (BitTheme._initOptions.darkTheme) { - BitTheme._darkTheme = BitTheme._initOptions.darkTheme; - } - - if (BitTheme._initOptions.lightTheme) { - BitTheme._lightTheme = BitTheme._initOptions.lightTheme; - } - - let theme = BitTheme._initOptions.theme || BitTheme._initOptions.default || BitTheme._lightTheme; - - if (BitTheme._initOptions.system) { - theme = BitTheme.isSystemDark() ? BitTheme._darkTheme : BitTheme._lightTheme; - } - - if (BitTheme._initOptions.persist) { - BitTheme._persist = true; - theme = BitTheme.getPersisted() || theme; - } - - BitTheme.set(theme); - } - - public static onChange(fn: onThemeChangeType) { - BitTheme._onThemeChange = fn; - } - - public static get() { - BitTheme._currentTheme = document.documentElement.getAttribute(BitTheme.THEME_ATTRIBUTE) || ''; - - if (BitTheme._persist) { - var theme = BitTheme.getActualTheme(BitTheme.getPersisted()); - BitTheme._currentTheme = theme || BitTheme._currentTheme; - } - - return BitTheme._currentTheme; - } - - public static set(themeName: string) { - BitTheme._currentTheme = BitTheme.getActualTheme(themeName)!; - - if (BitTheme._persist) { - localStorage.setItem(BitTheme.THEME_STORAGE_KEY, themeName); - } - - const oldTheme = document.documentElement.getAttribute(BitTheme.THEME_ATTRIBUTE) || ''; - - document.documentElement.setAttribute(BitTheme.THEME_ATTRIBUTE, BitTheme._currentTheme); - - BitTheme._onThemeChange?.(BitTheme._currentTheme, oldTheme); - - return BitTheme._currentTheme; - } - - public static toggleDarkLight() { - BitTheme._currentTheme = BitTheme._currentTheme === BitTheme._lightTheme - ? BitTheme._darkTheme - : BitTheme._lightTheme; - - BitTheme.set(BitTheme._currentTheme); - - return BitTheme._currentTheme; - } - - public static applyBitTheme(theme: any, element?: HTMLElement) { - const el = element || document.body; - Object.keys(theme).forEach(key => el.style.setProperty(key, theme[key])); - } - - public static isSystemDark() { - return matchMedia('(prefers-color-scheme: dark)').matches; - } - - public static getPersisted() { - if (!BitTheme._persist) return null; - - return localStorage.getItem(BitTheme.THEME_STORAGE_KEY); - } - - private static getActualTheme(theme: string | null) { - if (theme === BitTheme.SYSTEM_THEME) { - return BitTheme.isSystemDark() ? BitTheme._darkTheme : BitTheme._lightTheme; - } - - return theme; - } -} - -(function () { - const options = { - system: document.documentElement.hasAttribute('bit-theme-system'), - persist: document.documentElement.hasAttribute('bit-theme-persist'), - theme: document.documentElement.getAttribute('bit-theme'), - default: document.documentElement.getAttribute('bit-theme-default'), - darkTheme: document.documentElement.getAttribute('bit-theme-dark'), - lightTheme: document.documentElement.getAttribute('bit-theme-light'), - }; - - BitTheme.init(options); -}()); - -(window as any).BitTheme = BitTheme; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor index 7dfa6cf74f..4be88cf46b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor @@ -450,6 +450,39 @@ +

+
Disabled:
+
+
+ Primary + Secondary + Tertiary + Info + Success + Warning + SevereWarning + Error +
+

+
+
+ PrimaryBackground + SecondaryBackground + TertiaryBackground +
+
+
+
+ PrimaryForeground + SecondaryForeground + TertiaryForeground +
+
+
+ PrimaryBorder + SecondaryBorder + TertiaryBorder +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs index df5842a0fc..a285ea4547 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ActionButton/BitActionButtonDemo.razor.samples.cs @@ -289,7 +289,29 @@ More info TertiaryBorder -"; + + + +Primary +Secondary +Tertiary +Info +Success +Warning +SevereWarning +Error + +PrimaryBackground +SecondaryBackground +TertiaryBackground + +PrimaryForeground +SecondaryForeground +TertiaryForeground + +PrimaryBorder +SecondaryBorder +TertiaryBorder"; private readonly string example12RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor index ff1fb059c7..f1618a1629 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor @@ -588,6 +588,116 @@ TertiaryBorder +

+
Disabled:
+
+
+ Primary + Primary + Primary +
+
+
+ Secondary + Secondary + Secondary +
+
+
+ Tertiary + Tertiary + Tertiary +
+
+
+ Info + Info + Info +
+
+
+ Success + Success + Success +
+
+
+ Warning + Warning + Warning +
+
+
+ SevereWarning + SevereWarning + SevereWarning +
+
+
+ Error + Error + Error +
+

+
+
+ PrimaryBackground + PrimaryBackground + PrimaryBackground +
+
+
+ SecondaryBackground + SecondaryBackground + SecondaryBackground +
+
+
+ TertiaryBackground + TertiaryBackground + TertiaryBackground +
+
+
+
+
+ PrimaryForeground + PrimaryForeground + PrimaryForeground +
+
+
+ SecondaryForeground + SecondaryForeground + SecondaryForeground +
+
+
+ TertiaryForeground + TertiaryForeground + TertiaryForeground +
+
+
+
+
+ PrimaryBorder + PrimaryBorder + PrimaryBorder +
+
+
+ SecondaryBorder + SecondaryBorder + SecondaryBorder +
+
+
+ TertiaryBorder + TertiaryBorder + TertiaryBorder +
+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor.samples.cs index 9946817d4d..8e3a0490ac 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/Button/BitButtonDemo.razor.samples.cs @@ -545,7 +545,78 @@ private async Task LoadingTemplateClick() TertiaryBorder TertiaryBorder -TertiaryBorder"; +TertiaryBorder + + +Disabled: + +Primary +Primary +Primary + +Secondary +Secondary +Secondary + +Tertiary +Tertiary +Tertiary + +Info +Info +Info + +Success +Success +Success + +Warning +Warning +Warning + +SevereWarning +SevereWarning +SevereWarning + +Error +Error +Error + +PrimaryBackground +PrimaryBackground +PrimaryBackground + +SecondaryBackground +SecondaryBackground +SecondaryBackground + +TertiaryBackground +TertiaryBackground +TertiaryBackground + +PrimaryForeground +PrimaryForeground +PrimaryForeground + +SecondaryForeground +SecondaryForeground +SecondaryForeground + +TertiaryForeground +TertiaryForeground +TertiaryForeground + +PrimaryBorder +PrimaryBorder +PrimaryBorder + +SecondaryBorder +SecondaryBorder +SecondaryBorder + +TertiaryBorder +TertiaryBorder +TertiaryBorder"; private readonly string example18RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/BitButtonGroupDemo.razor.scss b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/BitButtonGroupDemo.razor.scss index daf5d6992e..1581699430 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/BitButtonGroupDemo.razor.scss +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/BitButtonGroupDemo.razor.scss @@ -3,6 +3,11 @@ gap: 0.5rem; display: flex; flex-flow: column; + + &.row { + flex-flow: row; + flex-wrap: wrap; + } } .custom-class { @@ -32,4 +37,4 @@ border-color: aliceblue; background-color: crimson; } -} +} \ No newline at end of file diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor index 7a2a029365..a04dd9f48f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor @@ -358,6 +358,129 @@ +

+
Disabled:
+
+
+
Primary
+ + + +
+

+
+
Secondary
+ + + +
+

+
+
Tertiary
+ + + +
+

+
+
Info
+ + + +
+

+
+
Success
+ + + +
+

+
+
Warning
+ + + +
+

+
+
SevereWarning
+ + + +
+

+
+
Error
+ + + +
+

+
+
+
PrimaryBackground
+ + + +
+

+
+
SecondaryBackground
+ + + +
+

+
+
TertiaryBackground
+ + + +
+
+

+
+
PrimaryForeground
+ + + +
+

+
+
SecondaryForeground
+ + + +
+

+
+
TertiaryForeground
+ + + +
+

+
+
PrimaryBorder
+ + + +
+

+
+
SecondaryBorder
+ + + +
+

+
+
TertiaryBorder
+ + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.samples.cs index 82bfe511f8..8128506deb 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupCustomDemo.razor.samples.cs @@ -375,7 +375,81 @@ public class Operation -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example11CsharpCode = @" private BitButtonGroupNameSelectors nameSelector = new() { Text = { Selector = i => i.Name } }; diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor index 1a1c463421..f18068e9b6 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor @@ -206,126 +206,249 @@
Offering a range of specialized colors, providing visual cues for specific states within your application.


-
-
Primary
+
Primary
+


-
-
Secondary
+
Secondary
+


-
-
Tertiary
+
Tertiary
+


-
-
Info
+
Info
+


-
-
Success
+
Success
+


-
-
Warning
+
Warning
+


-
-
SevereWarning
+
SevereWarning
+


-
-
Error
+
Error
+


-
-
PrimaryBackground
+
PrimaryBackground
+


-
-
SecondaryBackground
+
SecondaryBackground
+


-
-
TertiaryBackground
+
TertiaryBackground
+


-
-
PrimaryForeground
+
PrimaryForeground
+


-
-
SecondaryForeground
+
SecondaryForeground
+


-
-
TertiaryForeground
+
TertiaryForeground
+


-
-
PrimaryBorder
+
PrimaryBorder
+


-
-
SecondaryBorder
+
SecondaryBorder
+


-
-
TertiaryBorder
+
TertiaryBorder
+
+



+
Disabled:
+
+
Primary
+
+ + + +
+

+
Secondary
+
+ + + +
+

+
Tertiary
+
+ + + +
+

+
Info
+
+ + + +
+

+
Success
+
+ + + +
+

+
Warning
+
+ + + +
+

+
SevereWarning
+
+ + + +
+

+
Error
+
+ + + +
+

+
+
PrimaryBackground
+
+ + + +
+

+
SecondaryBackground
+
+ + + +
+

+
TertiaryBackground
+
+ + + +
+
+

+
PrimaryForeground
+
+ + + +
+

+
SecondaryForeground
+
+ + + +
+

+
TertiaryForeground
+
+ + + +
+

+
PrimaryBorder
+
+ + + +
+

+
SecondaryBorder
+
+ + + +
+

+
TertiaryBorder
+
+ + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor.samples.cs index c442e952ee..61a58380fa 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupItemDemo.razor.samples.cs @@ -240,7 +240,81 @@ protected override void OnInitialized() -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example11CsharpCode = @" private List basicItems = [ diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor index 52ce3ef8e1..8bb7ce4bfb 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor @@ -557,6 +557,231 @@
+

+
Disabled:
+
+
+
Primary
+ + + + + + + + + +
+

+
+
Secondary
+ + + + + + + + + +
+

+
+
Tertiary
+ + + + + + + + + +
+

+
+
Info
+ + + + + + + + + +
+

+
+
Success
+ + + + + + + + + +
+

+
+
Warning
+ + + + + + + + + +
+

+
+
SevereWarning
+ + + + + + + + + +
+

+
+
Error
+ + + + + + + + + +
+

+
+
+
PrimaryBackground
+ + + + + + + + + +
+

+
+
SecondaryBackground
+ + + + + + + + + +
+

+
+
TertiaryBackground
+ + + + + + + + + +
+
+

+
+
PrimaryForeground
+ + + + + + + + + +
+

+
+
SecondaryForeground
+ + + + + + + + + +
+

+
+
TertiaryForeground
+ + + + + + + + + +
+

+
+
PrimaryBorder
+ + + + + + + + + +
+

+
+
SecondaryBorder
+ + + + + + + + + +
+

+
+
TertiaryBorder
+ + + + + + + + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor.samples.cs index f031af76c5..c28039649f 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ButtonGroup/_BitButtonGroupOptionDemo.razor.samples.cs @@ -400,6 +400,182 @@ public partial class _BitButtonGroupOptionDemo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; private readonly string example12RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor index fb6d55c35d..5d90f3922a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor @@ -519,6 +519,214 @@
+

+
Disabled:
+
+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs index 283ddbc6c8..8040064a03 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonCustomDemo.razor.samples.cs @@ -726,7 +726,162 @@ public class Operation -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example14CsharpCode = @" public class Operation { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor index efa7677d3d..f7b24179f4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor @@ -509,6 +509,214 @@
+

+
Disabled:
+
+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
+

+
+ + + +
+
+
+ + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor.samples.cs index 57f3c45209..7b353bca0a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonItemDemo.razor.samples.cs @@ -1,4 +1,4 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons.MenuButton; +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons.MenuButton; public partial class _BitMenuButtonItemDemo { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor index 4b166f3acf..cbfb227e37 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor @@ -1232,6 +1232,622 @@
+

+
Disabled:
+
+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+

+
+ + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor.cs index 760802398d..80e0286e03 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/MenuButton/_BitMenuButtonOptionDemo.razor.cs @@ -1,4 +1,4 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons.MenuButton; +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Buttons.MenuButton; public partial class _BitMenuButtonOptionDemo { @@ -1122,6 +1122,569 @@ Custom Header! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; private readonly string example15RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor index e937028327..cea903c782 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor @@ -272,6 +272,112 @@ TertiaryBorder TertiaryBorder
+

+
Disabled:
+
+
+ Primary + Primary + Primary +
+
+
+ Secondary + Secondary + Secondary +
+
+
+ Tertiary + Tertiary + Tertiary +
+
+
+ Info + Info + Info +
+
+
+ Success + Success + Success +
+
+
+ Warning + Warning + Warning +
+
+
+ SevereWarning + SevereWarning + SevereWarning +
+
+
+ Error + Error + Error +
+

+
+
+ PrimaryBackground + PrimaryBackground + PrimaryBackground +
+
+
+ SecondaryBackground + SecondaryBackground + SecondaryBackground +
+
+
+ TertiaryBackground + TertiaryBackground + TertiaryBackground +
+
+
+
+ PrimaryForeground + PrimaryForeground + PrimaryForeground +
+
+
+ SecondaryForeground + SecondaryForeground + SecondaryForeground +
+
+
+ TertiaryForeground + TertiaryForeground + TertiaryForeground +
+
+
+ PrimaryBorder + PrimaryBorder + PrimaryBorder +
+
+
+ SecondaryBorder + SecondaryBorder + SecondaryBorder +
+
+
+ TertiaryBorder + TertiaryBorder + TertiaryBorder +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor.samples.cs index a29dc5df58..8c62eb6a41 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Buttons/ToggleButton/BitToggleButtonDemo.razor.samples.cs @@ -182,7 +182,76 @@ Click me (@clickCounter) TertiaryBorder TertiaryBorder -TertiaryBorder"; +TertiaryBorder + + +Primary +Primary +Primary + +Secondary +Secondary +Secondary + +Tertiary +Tertiary +Tertiary + +Info +Info +Info + +Success +Success +Success + +Warning +Warning +Warning + +SevereWarning +SevereWarning +SevereWarning + +Error +Error +Error + +PrimaryBackground +PrimaryBackground +PrimaryBackground + +SecondaryBackground +SecondaryBackground +SecondaryBackground + +TertiaryBackground +TertiaryBackground +TertiaryBackground + +PrimaryForeground +PrimaryForeground +PrimaryForeground + +SecondaryForeground +SecondaryForeground +SecondaryForeground + +TertiaryForeground +TertiaryForeground +TertiaryForeground + +PrimaryBorder +PrimaryBorder +PrimaryBorder + +SecondaryBorder +SecondaryBorder +SecondaryBorder + +TertiaryBorder +TertiaryBorder +TertiaryBorder"; private readonly string example10RazorCode = @"
+

+
Disabled:
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+

+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Checkbox/BitCheckboxDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Checkbox/BitCheckboxDemo.razor.samples.cs index 7bcb7efa0a..eaf4f8cd01 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Checkbox/BitCheckboxDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/Checkbox/BitCheckboxDemo.razor.samples.cs @@ -181,7 +181,76 @@ private async Task HandleValidSubmit() { } -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example11RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor index 4e545126b5..b394550ae8 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor @@ -483,8 +483,182 @@
+

+
Disabled:
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
Use icons from external libraries like FontAwesome or Bootstrap Icons with the Icon parameter and NameSelectors.
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.samples.cs index f7a5e2e121..ed204a267b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupCustomDemo.razor.samples.cs @@ -1,4 +1,4 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Inputs.ChoiceGroup; +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Inputs.ChoiceGroup; public partial class _BitChoiceGroupCustomDemo { @@ -767,6 +767,143 @@ public class Order Horizontal Items=""basicCustoms"" DefaultValue=""basicCustoms[1].ItemValue"" + NameSelectors=""@(new() { Text = { Name = nameof(Order.Name) }, Value = { Name = nameof(Order.ItemValue) } })"" /> + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + +"; private readonly string example11CsharpCode = @" public class Order diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor index 61259174a1..45c5420955 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor @@ -362,8 +362,165 @@
+

+
Disabled:
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
Use icons from external libraries like FontAwesome or Bootstrap Icons with the Icon parameter.
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.samples.cs index fd446cc780..ccac9dd592 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupItemDemo.razor.samples.cs @@ -1,4 +1,4 @@ -namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Inputs.ChoiceGroup; +namespace Bit.BlazorUI.Demo.Client.Core.Pages.Components.Inputs.ChoiceGroup; public partial class _BitChoiceGroupItemDemo { @@ -556,7 +556,125 @@ Horizontal Inline /> Label=""TertiaryBorder"" Horizontal Items=""basicItems"" - DefaultValue=""basicItems[1].Value"" />"; + DefaultValue=""basicItems[1].Value"" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example11CsharpCode = @" private readonly List> basicItems = [ diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor index d02cadf1c4..e0bbc428d4 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor @@ -567,8 +567,166 @@
+

+
Disabled:
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
+
+ + + + + + +
+
Use icons from external libraries like FontAwesome or Bootstrap Icons with the Icon parameter.
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.samples.cs index a4ae96f13c..782a0f36cc 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/ChoiceGroup/_BitChoiceGroupOptionDemo.razor.samples.cs @@ -523,6 +523,125 @@ private void HandleValidSubmit() { } + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + + + +"" TValue=""string"" Horizontal> + + + + "; private readonly string example12RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor index bb9f7189dd..915e7ecc15 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor @@ -316,6 +316,78 @@
+

+
Disabled:
+
+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+
+ +
+ +

+ +
+ +

+ +
+ +
+

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+ +

+ +
+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.samples.cs index 29b17d7f8a..89fb4dbcb0 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Inputs/SearchBox/BitSearchBoxDemo.razor.samples.cs @@ -238,8 +238,108 @@ public class ValidationSearchBoxModel private ValidationSearchBoxModel validationBoxModel = new();"; private readonly string example12RazorCode = @" - -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example13RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor index 992d13b4ce..26ac7a0127 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor @@ -127,6 +127,64 @@
+

+
Disabled:
+
+
Primary

+ +
+ +
+ +


+
Secondary

+ +
+ +
+ +


+
Tertiary

+ +
+ +
+ +


+
Info

+ +
+ +
+ +


+
Success

+ +
+ +
+ +


+
Warning

+ +
+ +
+ +


+
SevereWarning

+ +
+ +
+ +


+
Error

+ +
+ +
+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs index 976f3436c5..c1ecf0c51c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineCustomDemo.razor.samples.cs @@ -272,7 +272,42 @@ public class Event -"; + + + +Disabled: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example8CsharpCode = @" public class Event { diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor index d6b6cb75eb..560adfb32c 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor @@ -127,6 +127,64 @@
+

+
Disabled:
+
+
Primary

+ +
+ +
+ +


+
Secondary

+ +
+ +
+ +


+
Tertiary

+ +
+ +
+ +


+
Info

+ +
+ +
+ +


+
Success

+ +
+ +
+ +


+
Warning

+ +
+ +
+ +


+
SevereWarning

+ +
+ +
+ +


+
Error

+ +
+ +
+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor.samples.cs index ef12776e10..fb42740b15 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineItemDemo.razor.samples.cs @@ -175,7 +175,42 @@ public partial class _BitTimelineItemDemo -"; + + + +Disabled: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example8CsharpCode = @" private List iconItems = [ diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor index 1904596ffb..10fdebd06a 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor @@ -393,6 +393,160 @@ +

+
Disabled:
+
+
Primary

+ + + + + +
+ + + + + +
+ + + + + +


+
Secondary

+ + + + + +
+ + + + + +
+ + + + + +


+
Tertiary

+ + + + + +
+ + + + + +
+ + + + + +


+
Info

+ + + + + +
+ + + + + +
+ + + + + +


+
Success

+ + + + + +
+ + + + + +
+ + + + + +


+
Warning

+ + + + + +
+ + + + + +
+ + + + + +


+
SevereWarning

+ + + + + +
+ + + + + +
+ + + + + +


+
Error

+ + + + + +
+ + + + + +
+ + + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor.cs index ed8cb21826..d87e3f80bb 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Lists/Timeline/_BitTimelineOptionDemo.razor.cs @@ -371,6 +371,160 @@ public partial class _BitTimelineOptionDemo + + + +Disabled: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; private readonly string example9RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor index a6f9e728eb..2768511ba2 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor @@ -119,6 +119,64 @@

+



+
Disabled:
+

+
Primary

+ +

+ +

+ +



+
Secondary

+ +

+ +

+ +



+
Tertiary

+ +

+ +

+ +



+
Info

+ +

+ +

+ +



+
Success

+ +

+ +

+ +



+
Warning

+ +

+ +

+ +



+
SevereWarning

+ +

+ +

+ +



+
Error

+ +

+ +

+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.samples.cs index 16dbd583c4..420127d047 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pagination/BitPaginationDemo.razor.samples.cs @@ -56,7 +56,42 @@ public partial class BitPaginationDemo -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example9RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor index a762abb8bf..fe0f1e2fab 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor @@ -535,77 +535,160 @@ +
Offering a range of specialized colors, providing visual cues for specific states within your application.
+
Using the Color parameter and its value of type BitColor enum:
+
+ +
Primary:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
Secondary:
+
- -

Pivot #1: Primary

-
- Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams. - Each word carried meaning, each pause brought understanding. Placeholder text reminds us of that moment - when possibilities are limitless, waiting for content to emerge. The spaces here are open for growth, - for ideas that change minds and spark emotions. This is where the journey begins—your words will lead the way. -
-
- -

Pivot #2: Secondary

-
- Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. - These placeholder words symbolize the beginning—a moment of possibility where creativity has yet to take shape. - Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and - inspirations will be built. Soon, these lines will transform into narratives that provoke thought, - spark emotion, and resonate with those who encounter them. Until then, they remind us of the beauty - in potential—the quiet magic of beginnings, where everything is still to come, and the possibilities - are boundless. This space is yours to craft, yours to shape, yours to bring to life. -
-
- -

Pivot #3: Info

-
- In the beginning, there is silence—a blank canvas yearning to be filled, a quiet space where creativity waits - to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite - possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the - vibrant narratives of tomorrow. It whispers of the stories waiting to be told, of the thoughts yet to be - shaped into meaning, and the emotions ready to resonate with every reader. -
-
+
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
Tertiary:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
Info:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
Success:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
Warning:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
SevereWarning:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+

+ +
Error:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+


+ +
Tab header type with role colors:
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent

- -

Pivot #1: Primary background

-
- Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams. - Each word carried meaning, each pause brought understanding. Placeholder text reminds us of that moment - when possibilities are limitless, waiting for content to emerge. The spaces here are open for growth, - for ideas that change minds and spark emotions. This is where the journey begins—your words will lead the way. -
-
- -

Pivot #2: Secondary background

-
- Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. - These placeholder words symbolize the beginning—a moment of possibility where creativity has yet to take shape. - Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and - inspirations will be built. Soon, these lines will transform into narratives that provoke thought, - spark emotion, and resonate with those who encounter them. Until then, they remind us of the beauty - in potential—the quiet magic of beginnings, where everything is still to come, and the possibilities - are boundless. This space is yours to craft, yours to shape, yours to bring to life. -
-
- -

Pivot #3: Tertiary foreground

-
- In the beginning, there is silence—a blank canvas yearning to be filled, a quiet space where creativity waits - to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite - possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the - vibrant narratives of tomorrow. It whispers of the stories waiting to be told, of the thoughts yet to be - shaped into meaning, and the emotions ready to resonate with every reader. -
-
+
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+
+
+ + +
Disabled pivot:
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+


+ +
Disabled pivot item:
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+


+ +
Disabled tab:
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+


+ +
Disabled tab item:
+
+ +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs index 647b8dafe3..9c63734086 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Navs/Pivot/BitPivotDemo.razor.cs @@ -1107,72 +1107,71 @@ These placeholder words symbolize the beginning—a moment of possibility where "; private readonly string example11RazorCode = @" + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ - -

Pivot #1: Primary

-
- Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams. - Each word carried meaning, each pause brought understanding. Placeholder text reminds us of that moment - when possibilities are limitless, waiting for content to emerge. The spaces here are open for growth, - for ideas that change minds and spark emotions. This is where the journey begins—your words will lead the way. -
-
- -

Pivot #2: Secondary

-
- Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. - These placeholder words symbolize the beginning—a moment of possibility where creativity has yet to take shape. - Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and - inspirations will be built. Soon, these lines will transform into narratives that provoke thought, - spark emotion, and resonate with those who encounter them. Until then, they remind us of the beauty - in potential—the quiet magic of beginnings, where everything is still to come, and the possibilities - are boundless. This space is yours to craft, yours to shape, yours to bring to life. -
-
- -

Pivot #3: Info

-
- In the beginning, there is silence—a blank canvas yearning to be filled, a quiet space where creativity waits - to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite - possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the - vibrant narratives of tomorrow. It whispers of the stories waiting to be told, of the thoughts yet to be - shaped into meaning, and the emotions ready to resonate with every reader. -
-
+
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
- -

Pivot #1: Primary background

-
- Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams. - Each word carried meaning, each pause brought understanding. Placeholder text reminds us of that moment - when possibilities are limitless, waiting for content to emerge. The spaces here are open for growth, - for ideas that change minds and spark emotions. This is where the journey begins—your words will lead the way. -
-
- -

Pivot #2: Secondary background

-
- Every story starts with a blank canvas, a quiet space waiting to be filled with ideas, emotions, and dreams. - These placeholder words symbolize the beginning—a moment of possibility where creativity has yet to take shape. - Imagine this text as the scaffolding of something remarkable, a foundation upon which connections and - inspirations will be built. Soon, these lines will transform into narratives that provoke thought, - spark emotion, and resonate with those who encounter them. Until then, they remind us of the beauty - in potential—the quiet magic of beginnings, where everything is still to come, and the possibilities - are boundless. This space is yours to craft, yours to shape, yours to bring to life. -
-
- -

Pivot #3: Tertiary foreground

-
- In the beginning, there is silence—a blank canvas yearning to be filled, a quiet space where creativity waits - to awaken. These words are temporary, standing in place of ideas yet to come, a glimpse into the infinite - possibilities that lie ahead. Think of this text as a bridge, connecting the empty spaces of now with the - vibrant narratives of tomorrow. It whispers of the stories waiting to be told, of the thoughts yet to be - shaped into meaning, and the emotions ready to resonate with every reader. -
-
+
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
"; private readonly string example12RazorCode = @" @@ -1460,4 +1459,29 @@ These placeholder words symbolize the beginning—a moment of possibility where اصلی و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد. "; + + private readonly string example15RazorCode = @" + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
+ + +
Pivot #1: File
+
Pivot #2: Shared
+
Pivot #3: Recent
+
"; } diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor index 3cd4c188b0..789b626ddd 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor @@ -215,6 +215,112 @@ +

+
Disabled:
+
+
Primary

+
+ + + + + + + + + +
+

+
Secondary

+
+ + + + + + + + + +
+

+
Tertiary

+
+ + + + + + + + + +
+

+
Info

+
+ + + + + + + + + +
+

+
Success

+
+ + + + + + + + + +
+

+
Warning

+
+ + + + + + + + + +
+

+
SevereWarning

+
+ + + + + + + + + +
+

+
Error

+
+ + + + + + + + + +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor.cs index 29eb30b6ef..75c3dfb103 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Badge/BitBadgeDemo.razor.cs @@ -573,6 +573,87 @@ public partial class BitBadgeDemo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "; private readonly string example10RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor index 6f25ae28df..c58558bd34 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor @@ -116,6 +116,31 @@     +


+
Disabled:
+
+   +   +   +   +   +   +   + +

+
+   +   + +
+
+   +   + +

+   +   +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs index 22c0cd8d12..344372fa05 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Notifications/Tag/BitTagDemo.razor.cs @@ -409,7 +409,30 @@ public partial class BitTagDemo     -"; + + +Disabled: + + + + + + + + + + + + + + + + + + + + +"; private readonly string example6RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor index f834edbba0..55c2e204ce 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor @@ -129,6 +129,98 @@   + +



+ +
+
PrimaryBackground
+
+ +   + +   + + +



+ +
SecondaryBackground
+
+ +   + +   + + +



+ +
TertiaryBackground
+
+ +   + +   + +
+ +



+ +
PrimaryForeground
+
+ +   + +   + + +



+ +
SecondaryForeground
+
+ +   + +   + + +



+ +
TertiaryForeground
+
+ +   + +   + + +



+ +
PrimaryBorder
+
+ +   + +   + + +



+ +
SecondaryBorder
+
+ +   + +   + + +



+ +
TertiaryBorder
+
+ +   + +   +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs index fa28a82786..dee1b6e030 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Icon/BitIconDemo.razor.cs @@ -302,7 +302,43 @@ public partial class BitIconDemo -"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; private readonly string example4RazorCode = @" diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor index 1907d68fe1..973e4ca24b 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor @@ -146,6 +146,48 @@

TertiaryBorder Color Link +

+
Disabled:
+
+ Primary (default) +

+ Secondary +

+ Tertiary +

+ Info +

+ Success +

+ Warning +

+ SevereWarning +

+ Error +

+
+ PrimaryBackground +

+ SecondaryBackground +

+ TertiaryBackground +
+
+
+ PrimaryForeground +

+ SecondaryForeground +

+ TertiaryForeground +
+
+
+ PrimaryBorder +

+ SecondaryBorder +

+ TertiaryBorder +
diff --git a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.samples.cs b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.samples.cs index e444d10cf9..d734826593 100644 --- a/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.samples.cs +++ b/src/BlazorUI/Demo/Client/Bit.BlazorUI.Demo.Client.Core/Pages/Components/Utilities/Link/BitLinkDemo.razor.samples.cs @@ -106,7 +106,29 @@ and your voice a reflection of who you are and what you wish to share with the w PrimaryBorder Color Link SecondaryBorder Color Link -TertiaryBorder Color Link"; +TertiaryBorder Color Link + + +Primary (default) +Secondary +Tertiary +Info +Success +Warning +SevereWarning +Error + +PrimaryBackground +SecondaryBackground +TertiaryBackground + +PrimaryForeground +SecondaryForeground +TertiaryForeground + +PrimaryBorder +SecondaryBorder +TertiaryBorder"; private readonly string example10RazorCode = @"