diff --git a/apps/builder/app/builder/features/style-panel/sections/borders/border-style.tsx b/apps/builder/app/builder/features/style-panel/sections/borders/border-style.tsx
index b647b0a525..699080d3a5 100644
--- a/apps/builder/app/builder/features/style-panel/sections/borders/border-style.tsx
+++ b/apps/builder/app/builder/features/style-panel/sections/borders/border-style.tsx
@@ -1,13 +1,36 @@
import type { StyleProperty } from "@webstudio-is/css-data";
+import { toValue } from "@webstudio-is/css-engine";
+import {
+ Grid,
+ theme,
+ ToggleGroup,
+ ToggleGroupItem,
+ Tooltip,
+} from "@webstudio-is/design-system";
+import {
+ DashBorderIcon,
+ DashedBorderIcon,
+ DottedBorderIcon,
+ SmallXIcon,
+} from "@webstudio-is/icons";
+import { toPascalCase } from "../../shared/keyword-utils";
+import { PropertyName } from "../../shared/property-name";
import type { RenderCategoryProps } from "../../style-sections";
-import { BorderProperty } from "./border-property";
+import { deleteAllProperties, setAllProperties } from "./border-utils";
-const borderPropertyOptions = {
- borderTopStyle: {},
- borderRightStyle: {},
- borderLeftStyle: {},
- borderBottomStyle: {},
-} as const satisfies Partial<{ [property in StyleProperty]: unknown }>;
+const borderStyleProperties = [
+ "borderTopStyle",
+ "borderRightStyle",
+ "borderLeftStyle",
+ "borderBottomStyle",
+] as const satisfies readonly StyleProperty[];
+
+const borderStyleValues = [
+ { value: "none", Icon: SmallXIcon },
+ { value: "solid", Icon: DashBorderIcon },
+ { value: "dashed", Icon: DashedBorderIcon },
+ { value: "dotted", Icon: DottedBorderIcon },
+] as const;
export const BorderStyle = (
props: Pick<
@@ -15,14 +38,66 @@ export const BorderStyle = (
"currentStyle" | "setProperty" | "deleteProperty" | "createBatchUpdate"
>
) => {
+ /**
+ * We do not use shorthand properties such as borderWidth or borderRadius in our code.
+ * However, in the UI, we can display a single field, and in that case, we can use any property
+ * from the shorthand property set and pass it instead.
+ **/
+ const firstPropertyName = borderStyleProperties[0];
+
+ const deleteBorderProperties = deleteAllProperties(
+ borderStyleProperties,
+ props.createBatchUpdate
+ );
+
+ const setBorderProperties = setAllProperties(
+ borderStyleProperties,
+ props.createBatchUpdate
+ )(firstPropertyName);
+
+ const firstPropertyValue = toValue(
+ props.currentStyle[firstPropertyName]?.value ?? {
+ type: "keyword",
+ value: "none",
+ }
+ );
+
return (
-
+
+ deleteBorderProperties(firstPropertyName)}
+ />
+
+ {
+ setBorderProperties({
+ type: "keyword",
+ value,
+ });
+ }}
+ >
+ {borderStyleValues.map(({ value, Icon }) => (
+
+
+
+
+
+ ))}
+
+
);
};
diff --git a/apps/builder/app/builder/features/style-panel/sections/borders/borders.stories.tsx b/apps/builder/app/builder/features/style-panel/sections/borders/borders.stories.tsx
index 7bf9b10c33..9064991066 100644
--- a/apps/builder/app/builder/features/style-panel/sections/borders/borders.stories.tsx
+++ b/apps/builder/app/builder/features/style-panel/sections/borders/borders.stories.tsx
@@ -20,7 +20,6 @@ const styleInfoInitial: StyleInfo = {
const Panel = styled("div", {
width: theme.spacing[30],
boxSizing: "border-box",
- padding: theme.spacing[9],
});
export const Borders = () => {
diff --git a/apps/builder/app/builder/features/style-panel/sections/borders/borders.tsx b/apps/builder/app/builder/features/style-panel/sections/borders/borders.tsx
index 3f5c579fc0..1a2ccc8528 100644
--- a/apps/builder/app/builder/features/style-panel/sections/borders/borders.tsx
+++ b/apps/builder/app/builder/features/style-panel/sections/borders/borders.tsx
@@ -42,6 +42,13 @@ export const BordersSection = (props: RenderCategoryProps) => {
return (
+
+
{
-
-
;
};
diff --git a/packages/icons/icons/dash-border.svg b/packages/icons/icons/dash-border.svg
new file mode 100644
index 0000000000..3800a43236
--- /dev/null
+++ b/packages/icons/icons/dash-border.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/icons/icons/dashed-border.svg b/packages/icons/icons/dashed-border.svg
new file mode 100644
index 0000000000..b8713a3b1e
--- /dev/null
+++ b/packages/icons/icons/dashed-border.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/icons/icons/dotted-border.svg b/packages/icons/icons/dotted-border.svg
new file mode 100644
index 0000000000..b72136a3c8
--- /dev/null
+++ b/packages/icons/icons/dotted-border.svg
@@ -0,0 +1,6 @@
+
diff --git a/packages/icons/icons/small-x.svg b/packages/icons/icons/small-x.svg
new file mode 100644
index 0000000000..83438b01cc
--- /dev/null
+++ b/packages/icons/icons/small-x.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/icons/src/__generated__/dash-border.tsx b/packages/icons/src/__generated__/dash-border.tsx
new file mode 100644
index 0000000000..290295d1ab
--- /dev/null
+++ b/packages/icons/src/__generated__/dash-border.tsx
@@ -0,0 +1,15 @@
+// Generated from icons/dash-border.svg
+
+import { forwardRef } from "react";
+import type { IconProps } from "../types";
+
+// prettier-ignore
+export const DashBorderIcon = forwardRef(
+ ({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
+ return (
+
+ );
+ }
+);
+
+DashBorderIcon.displayName = "DashBorderIcon";
diff --git a/packages/icons/src/__generated__/dashed-border.tsx b/packages/icons/src/__generated__/dashed-border.tsx
new file mode 100644
index 0000000000..bf1cf29f99
--- /dev/null
+++ b/packages/icons/src/__generated__/dashed-border.tsx
@@ -0,0 +1,15 @@
+// Generated from icons/dashed-border.svg
+
+import { forwardRef } from "react";
+import type { IconProps } from "../types";
+
+// prettier-ignore
+export const DashedBorderIcon = forwardRef(
+ ({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
+ return (
+
+ );
+ }
+);
+
+DashedBorderIcon.displayName = "DashedBorderIcon";
diff --git a/packages/icons/src/__generated__/dotted-border.tsx b/packages/icons/src/__generated__/dotted-border.tsx
new file mode 100644
index 0000000000..06dfe81616
--- /dev/null
+++ b/packages/icons/src/__generated__/dotted-border.tsx
@@ -0,0 +1,15 @@
+// Generated from icons/dotted-border.svg
+
+import { forwardRef } from "react";
+import type { IconProps } from "../types";
+
+// prettier-ignore
+export const DottedBorderIcon = forwardRef(
+ ({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
+ return (
+
+ );
+ }
+);
+
+DottedBorderIcon.displayName = "DottedBorderIcon";
diff --git a/packages/icons/src/__generated__/index.ts b/packages/icons/src/__generated__/index.ts
index 328975102a..621859fd7b 100644
--- a/packages/icons/src/__generated__/index.ts
+++ b/packages/icons/src/__generated__/index.ts
@@ -27,9 +27,12 @@ export * from "./chevron-filled-right";
export * from "./chevron-filled-up";
export * from "./code";
export * from "./commit";
+export * from "./dash-border";
export * from "./dash";
+export * from "./dashed-border";
export * from "./div";
export * from "./dot";
+export * from "./dotted-border";
export * from "./drag-handle";
export * from "./eyecon-closed";
export * from "./eyecon-open";
@@ -51,6 +54,7 @@ export * from "./repeat-grid";
export * from "./repeat-row";
export * from "./scroll";
export * from "./slot-component";
+export * from "./small-x";
export * from "./subscript";
export * from "./subtract";
export * from "./superscript";
diff --git a/packages/icons/src/__generated__/small-x.tsx b/packages/icons/src/__generated__/small-x.tsx
new file mode 100644
index 0000000000..47e07dcac3
--- /dev/null
+++ b/packages/icons/src/__generated__/small-x.tsx
@@ -0,0 +1,15 @@
+// Generated from icons/small-x.svg
+
+import { forwardRef } from "react";
+import type { IconProps } from "../types";
+
+// prettier-ignore
+export const SmallXIcon = forwardRef(
+ ({ color = "currentColor", size = 16, ...props }, forwardedRef) => {
+ return (
+
+ );
+ }
+);
+
+SmallXIcon.displayName = "SmallXIcon";