diff --git a/packages/web/src/scss/components/Tooltip/_TooltipPopover.scss b/packages/web/src/scss/components/Tooltip/_TooltipPopover.scss
index 9ea1329fba..eb7085cc0b 100644
--- a/packages/web/src/scss/components/Tooltip/_TooltipPopover.scss
+++ b/packages/web/src/scss/components/Tooltip/_TooltipPopover.scss
@@ -1,5 +1,4 @@
-// 1. Although it may seem pointless, having the arrow as a standalone HTML element has a purpose: the arrow can be
-//    precisely positioned in sticky tooltips, e.g. with Floating UI:
+// 1. The arrow must be a standalone HTML element so the Floating UI library can position it correctly.
 //    https://floating-ui.com
 //
 // 2. Reuse already generated custom properties to allow overriding the arrow appearance live in the browser.
@@ -15,6 +14,7 @@
 
 .TooltipPopover {
     @include placement.child();
+    @include placement.child-controlled($prefix: 'tooltip', $offset: theme.$arrow-height);
     @include typography.generate(theme.$typography);
 
     --tooltip-max-width: #{theme.$max-width};
@@ -72,6 +72,7 @@
     $class-name: 'TooltipPopover',
     $dictionary-values: theme.$placement-dictionary,
     $offset: theme.$arrow-height,
+    $is-controlled: true,
     $has-arrow: true
 );
 
@@ -83,14 +84,3 @@
 .TooltipPopover.is-visible {
     @extend %visible;
 }
-
-// Controlled placement
-// stylelint-disable-next-line selector-max-class
-.TooltipPopover.TooltipPopover {
-    @include placement.child-controlled($prefix: 'tooltip', $offset: theme.$arrow-height);
-}
-
-// stylelint-disable-next-line selector-max-class
-.TooltipPopover.TooltipPopover .TooltipPopover__arrow {
-    @include placement.arrow-controlled();
-}
diff --git a/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss b/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss
index 6d547d2273..0cfa9cbc6d 100644
--- a/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss
+++ b/packages/web/src/scss/tools/__tests__/_dictionaries.test.scss
@@ -125,7 +125,12 @@
     @include test.it('should generate correct placement classes based on a dictionary') {
         @include test.assert() {
             @include test.output() {
-                @include dictionaries.generate-placements('Test', ('top-start'));
+                @include dictionaries.generate-placements(
+                    $class-name: 'Test',
+                    $dictionary-values: (
+                        'top-start',
+                    )
+                );
             }
 
             @include test.expect() {
@@ -141,7 +146,12 @@
 
         @include test.assert() {
             @include test.output() {
-                @include dictionaries.generate-placements('Test', ('right-end'));
+                @include dictionaries.generate-placements(
+                    $class-name: 'Test',
+                    $dictionary-values: (
+                        'right-end',
+                    )
+                );
             }
 
             @include test.expect() {
@@ -154,6 +164,56 @@
                 }
             }
         }
+
+        @include test.assert() {
+            @include test.output() {
+                @include dictionaries.generate-placements(
+                    $class-name: 'Test',
+                    $dictionary-values: (
+                        'top-start',
+                    ),
+                    $offset: 10px,
+                    $is-controlled: true
+                );
+            }
+
+            @include test.expect() {
+                .Test[data-spirit-placement='top-start'] {
+                    --test-offset: 10px;
+                    --test-offset-orthogonal: 0;
+
+                    transform-origin: bottom left;
+                }
+            }
+        }
+
+        @include test.assert() {
+            @include test.output() {
+                @include dictionaries.generate-placements(
+                    $class-name: 'Test',
+                    $dictionary-values: (
+                        'top-start',
+                    ),
+                    $offset: 10px,
+                    $is-controlled: true,
+                    $has-arrow: true
+                );
+            }
+
+            @include test.expect() {
+                .Test[data-spirit-placement='top-start'] {
+                    --test-offset: 10px;
+                    --test-offset-orthogonal: 0;
+
+                    transform-origin: bottom left;
+                }
+
+                .Test[data-spirit-placement='top-start'] > .Test__arrow {
+                    transform-origin: center;
+                    rotate: z 180deg;
+                }
+            }
+        }
     }
 }
 
diff --git a/packages/web/src/scss/tools/__tests__/_placement.test.scss b/packages/web/src/scss/tools/__tests__/_placement.test.scss
index b8cd493613..64b6294790 100644
--- a/packages/web/src/scss/tools/__tests__/_placement.test.scss
+++ b/packages/web/src/scss/tools/__tests__/_placement.test.scss
@@ -162,7 +162,7 @@
         @include test.assert() {
             @include test.output() {
                 .child-test {
-                    @include placement.child(3);
+                    @include placement.child($z-index: 3);
                 }
             }
             @include test.expect() {
@@ -174,11 +174,30 @@
         }
     }
 
+    @include test.it('should apply controlled styles to child') {
+        @include test.assert() {
+            @include test.output() {
+                .child-controlled-test {
+                    @include placement.child-controlled('test', 10px);
+                }
+            }
+            @include test.expect() {
+                .child-controlled-test {
+                    --test-offset-orthogonal: 0;
+                    --test-offset: 10px;
+
+                    inset: unset;
+                    translate: unset;
+                }
+            }
+        }
+    }
+
     @include test.it('should apply custom styles to child variant') {
         @include test.assert() {
             @include test.output() {
                 .child-variant-test {
-                    @include placement.child-variant('test', 'top', 10px);
+                    @include placement.child-variant($prefix: 'test', $placement: 'top', $offset: 10px);
                 }
             }
             @include test.expect() {
@@ -191,22 +210,24 @@
                 }
             }
         }
-    }
 
-    @include test.it('should apply controlled styles to child') {
         @include test.assert() {
             @include test.output() {
-                .child-controlled-test {
-                    @include placement.child-controlled('test', 10px);
+                .child-variant-test {
+                    @include placement.child-variant(
+                        $prefix: 'test',
+                        $placement: 'top',
+                        $offset: 10px,
+                        $is-controlled: true
+                    );
                 }
             }
             @include test.expect() {
-                .child-controlled-test {
-                    --test-offset-orthogonal: 0;
+                .child-variant-test {
                     --test-offset: 10px;
+                    --test-offset-orthogonal: 0;
 
-                    inset: unset;
-                    translate: unset;
+                    transform-origin: bottom;
                 }
             }
         }
@@ -248,22 +269,4 @@
             }
         }
     }
-
-    @include test.it('should apply controlled styles to arrow') {
-        @include test.assert() {
-            @include test.output() {
-                .arrow-controlled-test {
-                    @include placement.arrow-controlled();
-                }
-            }
-
-            @include test.expect() {
-                .arrow-controlled-test {
-                    inset: unset;
-                    translate: unset;
-                    transform-origin: center;
-                }
-            }
-        }
-    }
 }
diff --git a/packages/web/src/scss/tools/_dictionaries.scss b/packages/web/src/scss/tools/_dictionaries.scss
index b8b9435d8a..b782e612c9 100644
--- a/packages/web/src/scss/tools/_dictionaries.scss
+++ b/packages/web/src/scss/tools/_dictionaries.scss
@@ -184,17 +184,21 @@
 // * $dictionary-values: map of the placements to generate
 // * $offset: offset of child from its parent, typically for an arrow
 // * $has-arrow: whether the component has an arrow
-@mixin generate-placements($class-name, $dictionary-values, $offset: 0, $has-arrow: false) {
+@mixin generate-placements($class-name, $dictionary-values, $offset: 0, $is-controlled: false, $has-arrow: false) {
     $prefix: spirit-string.convert-pascal-case-to-kebab-case($class-name);
 
     @each $placement in $dictionary-values {
         .#{$class-name}[data-spirit-placement='#{$placement}'] {
-            @include placement.child-variant($prefix, $placement, $offset);
+            @include placement.child-variant($prefix, $placement, $offset, $is-controlled);
         }
 
         @if $has-arrow {
             .#{$class-name}[data-spirit-placement='#{$placement}'] > .#{$class-name}__arrow {
-                @include placement.arrow-variant($prefix, placement.transform($placement, $main-axis-inverse: true));
+                @include placement.arrow-variant(
+                    $prefix,
+                    placement.transform($placement, $main-axis-inverse: true),
+                    $is-controlled
+                );
             }
         }
     }
diff --git a/packages/web/src/scss/tools/_placement.scss b/packages/web/src/scss/tools/_placement.scss
index 7d95bb393b..e2d4e51805 100644
--- a/packages/web/src/scss/tools/_placement.scss
+++ b/packages/web/src/scss/tools/_placement.scss
@@ -207,16 +207,6 @@ $_logical-to-physical-placement-map: (
     z-index: $z-index;
 }
 
-@mixin child-variant($prefix, $placement, $offset: 0) {
-    --#{$prefix}-offset: #{$offset}; // 1.3.a
-
-    inset: map.get($_inset-map, $placement); // 1.2
-    translate: map.get(-get-child-translate-map($prefix), $placement); // 1.3
-    transform-origin: string.unquote(
-        transform($placement, $main-axis-inverse: true, $cross-axis-physical: true, $join-with: ' ')
-    );
-}
-
 @mixin child-controlled($prefix, $offset) {
     --#{$prefix}-offset-orthogonal: 0; // 2.
     --#{$prefix}-offset: #{$offset}; // 2.
@@ -225,6 +215,21 @@ $_logical-to-physical-placement-map: (
     translate: unset; // 2.
 }
 
+@mixin child-variant($prefix, $placement, $offset: 0, $is-controlled: false) {
+    --#{$prefix}-offset: #{$offset}; // 1.3.a, 2.
+
+    @if $is-controlled {
+        --#{$prefix}-offset-orthogonal: 0; // 2.
+    } @else {
+        inset: map.get($_inset-map, $placement); // 1.2
+        translate: map.get(-get-child-translate-map($prefix), $placement); // 1.3
+    }
+
+    transform-origin: string.unquote(
+        transform($placement, $main-axis-inverse: true, $cross-axis-physical: true, $join-with: ' ')
+    );
+}
+
 @mixin arrow($prefix, $width, $height, $corner-offset) {
     --#{$prefix}-arrow-width: #{$width};
     --#{$prefix}-arrow-height: #{$height};
@@ -234,14 +239,13 @@ $_logical-to-physical-placement-map: (
     transform-origin: bottom center;
 }
 
-@mixin arrow-variant($prefix, $placement) {
-    inset: map.get($_inset-map, $placement); // 1.2
-    translate: map.get(-get-arrow-translate-map($prefix), $placement); // 1.3
-    rotate: z map.get($_arrow-rotate-map, $placement);
-}
+@mixin arrow-variant($prefix, $placement, $is-controlled: false) {
+    @if $is-controlled {
+        transform-origin: center; // 2.
+    } @else {
+        inset: map.get($_inset-map, $placement); // 1.2
+        translate: map.get(-get-arrow-translate-map($prefix), $placement); // 1.3
+    }
 
-@mixin arrow-controlled() {
-    inset: unset; // 2.
-    translate: unset; // 2.
-    transform-origin: center; // 2.
+    rotate: z map.get($_arrow-rotate-map, $placement);
 }