diff --git a/docs/components/range.md b/docs/components/range.md
index 963bcbf42..9d4f27fcd 100644
--- a/docs/components/range.md
+++ b/docs/components/range.md
@@ -5,7 +5,7 @@
Ranges allow the user to select a single value within a given range using a slider.
```html preview
-
+
```
?> This component doesn't work with standard forms. Use [``](/components/form) instead.
@@ -36,6 +36,17 @@ To disable the tooltip, set `tooltip` to `none`.
```
+### Custom Track Colors
+
+You can customize the active and inactive portions of the track using the `--track-color-active` and `--track-color-inactive` custom properties.
+
+```html preview
+
+```
+
### Custom Tooltip Formatter
You can change the tooltip's content by setting the `tooltipFormatter` property to a function that accepts the range's value as an argument.
diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md
index dc0f40e32..446e8e9c6 100644
--- a/docs/resources/changelog.md
+++ b/docs/resources/changelog.md
@@ -17,6 +17,8 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
- Added the `filled` variation to ``, ``, and `` and supporting design tokens
- Added the `control` part to `` so you can target the main control with CSS [#538](https://github.com/shoelace-style/shoelace/issues/538)
- Added a border to `` to improve contrast when drawn on various background colors
+- Added `--track-color-active` and `--track-color-inactive` custom properties to `` [#550](https://github.com/shoelace-style/shoelace/issues/550)
+- Added the undocumented custom properties `--thumb-size`, `--tooltip-offset`, `--track-height` on ``
- Changed the default `distance` in `` from `2` to `0` [#538](https://github.com/shoelace-style/shoelace/issues/538)
- Fixed a bug where `` would be larger than the viewport when it had lots of options [#544](https://github.com/shoelace-style/shoelace/issues/544)
- Updated the default height of `` from `16px` to `1rem` and added a subtle shadow to indicate depth
diff --git a/src/components/range/range.styles.ts b/src/components/range/range.styles.ts
index f1d6de32c..b095372e9 100644
--- a/src/components/range/range.styles.ts
+++ b/src/components/range/range.styles.ts
@@ -9,8 +9,9 @@ export default css`
:host {
--thumb-size: 20px;
- --tooltip-offset-y: 10px;
- --track-color: rgb(var(--sl-color-neutral-200));
+ --tooltip-offset: 10px;
+ --track-color-active: rgb(var(--sl-color-neutral-200));
+ --track-color-inactive: rgb(var(--sl-color-neutral-200));
--track-height: 6px;
display: block;
@@ -22,8 +23,9 @@ export default css`
.range__control {
-webkit-appearance: none;
+ border-radius: 3px;
width: 100%;
- height: var(--sl-input-height-medium);
+ height: var(--track-height);
background: transparent;
line-height: var(--sl-input-height-medium);
vertical-align: middle;
@@ -33,7 +35,6 @@ export default css`
.range__control::-webkit-slider-runnable-track {
width: 100%;
height: var(--track-height);
- background-color: var(--track-color);
border-radius: 3px;
border: none;
}
@@ -74,10 +75,16 @@ export default css`
border: 0;
}
+ .range__control::-moz-range-progress {
+ background-color: var(--track-color-active);
+ border-radius: 3px;
+ height: var(--track-height);
+ }
+
.range__control::-moz-range-track {
width: 100%;
height: var(--track-height);
- background-color: var(--track-color);
+ background-color: var(--track-color-inactive);
border-radius: 3px;
border: none;
}
@@ -161,7 +168,7 @@ export default css`
/* Tooltip on top */
.range--tooltip-top .range__tooltip {
- top: calc(-1 * var(--thumb-size) - var(--tooltip-offset-y));
+ top: calc(-1 * var(--thumb-size) - var(--tooltip-offset));
}
.range--tooltip-top .range__tooltip:after {
@@ -173,8 +180,9 @@ export default css`
/* Tooltip on bottom */
.range--tooltip-bottom .range__tooltip {
- bottom: calc(-1 * var(--thumb-size) - var(--tooltip-offset-y));
+ bottom: calc(-1 * var(--thumb-size) - var(--tooltip-offset));
}
+
.range--tooltip-bottom .range__tooltip:after {
border-bottom: var(--sl-tooltip-arrow-size) solid rgb(var(--sl-tooltip-background-color));
border-left: var(--sl-tooltip-arrow-size) solid transparent;
diff --git a/src/components/range/range.ts b/src/components/range/range.ts
index 918375d8b..a9fdc6ddf 100644
--- a/src/components/range/range.ts
+++ b/src/components/range/range.ts
@@ -25,6 +25,12 @@ let id = 0;
* @csspart base - The component's base wrapper.
* @csspart input - The native range input.
* @csspart tooltip - The range tooltip.
+ *
+ * @cssproperty --thumb-size - The size of the thumb.
+ * @cssproperty --tooltip-offset - The vertical distance the tooltip is offset from the track.
+ * @cssproperty --track-color-active - The color of the portion of the track that represents the current value.
+ * @cssproperty --track-color-inactive: The of the portion of the track that represents the remaining value.
+ * @cssproperty --track-height: The height of the track.
*/
@customElement('sl-range')
export default class SlRange extends LitElement {
@@ -82,7 +88,7 @@ export default class SlRange extends LitElement {
connectedCallback() {
super.connectedCallback();
this.handleSlotChange = this.handleSlotChange;
- this.resizeObserver = new ResizeObserver(() => this.syncTooltip());
+ this.resizeObserver = new ResizeObserver(() => this.syncRange());
this.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
if (this.value === undefined || this.value === null) this.value = this.min;
@@ -92,7 +98,7 @@ export default class SlRange extends LitElement {
this.handleSlotChange();
this.updateComplete.then(() => {
- this.syncTooltip();
+ this.syncRange();
this.resizeObserver.observe(this.input);
});
}
@@ -123,7 +129,7 @@ export default class SlRange extends LitElement {
this.value = Number(this.input.value);
emit(this, 'sl-change');
- requestAnimationFrame(() => this.syncTooltip());
+ requestAnimationFrame(() => this.syncRange());
}
handleBlur() {
@@ -162,18 +168,32 @@ export default class SlRange extends LitElement {
this.hasTooltip = false;
}
- syncTooltip() {
+ syncRange() {
+ const percent = Math.max(0, (this.value - this.min) / (this.max - this.min));
+
+ this.syncProgress(percent);
+
if (this.tooltip !== 'none') {
- const percent = Math.max(0, (this.value - this.min) / (this.max - this.min));
- const inputWidth = this.input.offsetWidth;
- const tooltipWidth = this.output.offsetWidth;
- const thumbSize = getComputedStyle(this.input).getPropertyValue('--thumb-size');
- const x = `calc(${inputWidth * percent}px - calc(calc(${percent} * ${thumbSize}) - calc(${thumbSize} / 2)))`;
- this.output.style.transform = `translateX(${x})`;
- this.output.style.marginLeft = `-${tooltipWidth / 2}px`;
+ this.syncTooltip(percent);
}
}
+ syncProgress(percent: number) {
+ this.input.style.background = `linear-gradient(to right, var(--track-color-active) 0%, var(--track-color-active) ${
+ percent * 100
+ }%, var(--track-color-inactive) ${percent * 100}%, var(--track-color-inactive) 100%)`;
+ }
+
+ syncTooltip(percent: number) {
+ const inputWidth = this.input.offsetWidth;
+ const tooltipWidth = this.output.offsetWidth;
+ const thumbSize = getComputedStyle(this.input).getPropertyValue('--thumb-size');
+ const x = `calc(${inputWidth * percent}px - calc(calc(${percent} * ${thumbSize}) - calc(${thumbSize} / 2)))`;
+
+ this.output.style.transform = `translateX(${x})`;
+ this.output.style.marginLeft = `-${tooltipWidth / 2}px`;
+ }
+
render() {
// NOTE - always bind value after min/max, otherwise it will be clamped
return renderFormControl(