diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7dca7b46bae..1b27ff055e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
 All notable changes to this project will be documented in this file.
 This project adheres to [Semantic Versioning](http://semver.org/).
 
+## [6.0.1] - 2025-01-13
+
+### Added
+- Support for HSL colors. [#4957](https://github.com/plotly/plotly.py/issues/4957)
+- Function to converge from HSL to RGB: `hsl_to_rgb`.
+
 ## [6.0.0rc0] - 2024-11-27
 
 ### Added
diff --git a/packages/python/plotly/_plotly_utils/colors/__init__.py b/packages/python/plotly/_plotly_utils/colors/__init__.py
index 794c20d2e52..d4c050865ef 100644
--- a/packages/python/plotly/_plotly_utils/colors/__init__.py
+++ b/packages/python/plotly/_plotly_utils/colors/__init__.py
@@ -322,7 +322,7 @@ def validate_colors(colors, colortype="tuple"):
             # color in the plotly colorscale. In resolving this issue we
             # will be removing the immediate line below
             colors = [colors_list[0]] + [colors_list[-1]]
-        elif "rgb" in colors or "#" in colors:
+        elif "rgb" in colors or "#" in colors or "hsl" in colors:
             colors = [colors]
         else:
             raise exceptions.PlotlyError(
@@ -355,6 +355,16 @@ def validate_colors(colors, colortype="tuple"):
 
             colors[j] = each_color
 
+        if "hsl" in each_color:
+            try:
+                each_color = color_parser(each_color, hsl_to_rgb)
+            except ValueError as e:
+                raise exceptions.PlotlyError(
+                    f"Invalid HSL color: {each_color}. {str(e)}"
+                )
+            each_color = color_parser(each_color, unconvert_from_RGB_255)
+            colors[j] = each_color
+
         if isinstance(each_color, tuple):
             for value in each_color:
                 if value > 1.0:
@@ -767,6 +777,68 @@ def hex_to_rgb(value):
     )
 
 
+def hsl_to_rgb(value):
+    """
+    Converts an HSL color to RGB
+
+    :param (string) value: HSL color string
+
+    :rtype (tuple) (r_value, g_value, b_value): tuple of rgb values
+    """
+
+    def _hue_to_rgb(p, q, t):
+        """
+        Helper function for hsl_to_rgb
+        """
+        if t < 0:
+            t += 1
+        if t > 1:
+            t -= 1
+        if t < 1 / 6:
+            return p + (q - p) * 6 * t
+        if t < 1 / 2:
+            return q
+        if t < 2 / 3:
+            return p + (q - p) * (2 / 3 - t) * 6
+        return p
+
+    value = value.replace(" ", "").lower()
+
+    # Validate the format
+    if not value.startswith("hsl(") or not value.endswith(")"):
+        raise ValueError(
+            f"Invalid HSL format: {value}. Expected format: 'hsl(h, s%, l%)'"
+        )
+
+    try:
+        # Parsing the hsl string (usually of the form "hsl(h, s%, l%)")
+        h, s, l = value.lstrip("hsl(").rstrip(")%").split(",")
+        h, s, l = float(h) / 360, float(s.strip("%")) / 100, float(l.strip("%")) / 100
+    except Exception:
+        raise ValueError(f"Malformed HSL string: {value}")
+
+    if not (0 <= s <= 1 and 0 <= l <= 1):
+        raise ValueError(
+            f"Saturation and lightness must be between 0% and 100%. Got: s={s*100}%, l={l*100}%"
+        )
+
+    if not (0 <= h <= 1):
+        raise ValueError(f"Hue must be between 0 and 360. Got: {h*360}")
+
+    # Convert HSL to RGB
+    if s == 0:  # Achromatic
+        r = g = b = l
+
+    else:
+        q = l * (1 + s) if l < 0.5 else l + s - l * s
+        p = 2 * l - q
+        r = _hue_to_rgb(p, q, h + 1 / 3)
+        g = _hue_to_rgb(p, q, h)
+        b = _hue_to_rgb(p, q, h - 1 / 3)
+
+    return (round(r * 255), round(g * 255), round(b * 255))
+
+
 def colorscale_to_colors(colorscale):
     """
     Extracts the colors from colorscale as a list
diff --git a/packages/python/plotly/plotly/graph_objs/layout/_title.py b/packages/python/plotly/plotly/graph_objs/layout/_title.py
index f1500f4c492..a3b1eeac13e 100644
--- a/packages/python/plotly/plotly/graph_objs/layout/_title.py
+++ b/packages/python/plotly/plotly/graph_objs/layout/_title.py
@@ -31,13 +31,13 @@ def automargin(self):
         margins. If `yref='paper'` then the margin will expand to
         ensure that the title doesn’t overlap with the edges of the
         container. If `yref='container'` then the margins will ensure
-        that the title doesn’t overlap with the plot area, tick labels,
-        and axis titles. If `automargin=true` and the margins need to
-        be expanded, then y will be set to a default 1 and yanchor will
-        be set to an appropriate default to ensure that minimal margin
-        space is needed. Note that when `yref='paper'`, only 1 or 0 are
-        allowed y values. Invalid values will be reset to the default
-        1.
+        that the title doesn’t overlap with the plot area, tick
+        labels, and axis titles. If `automargin=true` and the margins
+        need to be expanded, then y will be set to a default 1 and
+        yanchor will be set to an appropriate default to ensure that
+        minimal margin space is needed. Note that when `yref='paper'`,
+        only 1 or 0 are allowed y values. Invalid values will be reset
+        to the default 1.
 
         The 'automargin' property must be specified as a bool
         (either True, or False)
@@ -365,14 +365,14 @@ def _prop_descriptions(self):
             figure margins. If `yref='paper'` then the margin will
             expand to ensure that the title doesn’t overlap with
             the edges of the container. If `yref='container'` then
-            the margins will ensure that the title doesn’t overlap
-            with the plot area, tick labels, and axis titles. If
-            `automargin=true` and the margins need to be expanded,
-            then y will be set to a default 1 and yanchor will be
-            set to an appropriate default to ensure that minimal
-            margin space is needed. Note that when `yref='paper'`,
-            only 1 or 0 are allowed y values. Invalid values will
-            be reset to the default 1.
+            the margins will ensure that the title doesn’t
+            overlap with the plot area, tick labels, and axis
+            titles. If `automargin=true` and the margins need to be
+            expanded, then y will be set to a default 1 and yanchor
+            will be set to an appropriate default to ensure that
+            minimal margin space is needed. Note that when
+            `yref='paper'`, only 1 or 0 are allowed y values.
+            Invalid values will be reset to the default 1.
         font
             Sets the title font.
         pad
@@ -449,14 +449,14 @@ def __init__(
             figure margins. If `yref='paper'` then the margin will
             expand to ensure that the title doesn’t overlap with
             the edges of the container. If `yref='container'` then
-            the margins will ensure that the title doesn’t overlap
-            with the plot area, tick labels, and axis titles. If
-            `automargin=true` and the margins need to be expanded,
-            then y will be set to a default 1 and yanchor will be
-            set to an appropriate default to ensure that minimal
-            margin space is needed. Note that when `yref='paper'`,
-            only 1 or 0 are allowed y values. Invalid values will
-            be reset to the default 1.
+            the margins will ensure that the title doesn’t
+            overlap with the plot area, tick labels, and axis
+            titles. If `automargin=true` and the margins need to be
+            expanded, then y will be set to a default 1 and yanchor
+            will be set to an appropriate default to ensure that
+            minimal margin space is needed. Note that when
+            `yref='paper'`, only 1 or 0 are allowed y values.
+            Invalid values will be reset to the default 1.
         font
             Sets the title font.
         pad
diff --git a/packages/python/plotly/plotly/tests/test_core/test_colors/test_colors.py b/packages/python/plotly/plotly/tests/test_core/test_colors/test_colors.py
index 2fb8e7831d9..f6baa80380c 100644
--- a/packages/python/plotly/plotly/tests/test_core/test_colors/test_colors.py
+++ b/packages/python/plotly/plotly/tests/test_core/test_colors/test_colors.py
@@ -237,3 +237,37 @@ def test_n_colors(self):
         ]
 
         self.assertEqual(generated_colorscale, expected_colorscale)
+
+
+def test_hsl_support(self):
+    # Test valid HSL input
+    valid_hsl = "hsl(240, 100%, 50%)"  # Blue
+    expected_rgb = (0, 0, 255)
+    self.assertEqual(colors.hsl_to_rgb(valid_hsl), expected_rgb)
+
+    # Test HSL input with spaces
+    valid_hsl_with_spaces = "hsl( 120 , 100% , 25% )"  # Dark Green
+    expected_rgb_with_spaces = (0, 64, 0)
+    self.assertEqual(colors.hsl_to_rgb(valid_hsl_with_spaces), expected_rgb_with_spaces)
+
+    # Test malformed HSL input
+    invalid_hsl = "hsl(120, 100, 50%)"  # Missing '%'
+    with self.assertRaises(ValueError):
+        colors.hsl_to_rgb(invalid_hsl)
+
+    # Test HSL with out-of-range values
+    out_of_range_hsl = "hsl(400, 120%, 50%)"
+    with self.assertRaises(ValueError):
+        colors.hsl_to_rgb(out_of_range_hsl)
+
+    # Test validate_colors with a list containing HSL
+    colors_list = ["hsl(0, 100%, 50%)", "hsl(120, 100%, 25%)"]
+    expected_colors = [(255, 0, 0), (0, 64, 0)]
+    self.assertEqual(
+        colors.validate_colors(colors_list, colortype="rgb"), expected_colors
+    )
+
+    # Test validate_colors with an invalid HSL in the list
+    invalid_colors_list = ["hsl(0, 100%, 50%)", "hsl(120, 100, 25%)"]
+    with self.assertRaises(PlotlyError):
+        colors.validate_colors(invalid_colors_list)