diff --git a/frontend/src/v2/__tests__/v2Primitives.test.tsx b/frontend/src/v2/__tests__/v2Primitives.test.tsx
index 4486259e7..6438a2ff2 100644
--- a/frontend/src/v2/__tests__/v2Primitives.test.tsx
+++ b/frontend/src/v2/__tests__/v2Primitives.test.tsx
@@ -67,4 +67,23 @@ describe('D223 primitives', () => {
expect(container.querySelector('details.v2p-disc')).not.toBeNull();
expect(container.querySelector('details.v2p-disc svg.chev path')?.getAttribute('d')).toBe('m6 9 6 6 6-6');
});
+
+ test('the Settings frame is built from v2p- primitives alone, in the artboard order', () => {
+ render();
+ const frame = screen.getByTestId('v2p-settings-frame');
+ const groups = Array.from(frame.querySelectorAll('.v2p-gtitle'), (n) => n.textContent);
+ expect(groups).toEqual(['Account', 'Access', 'Look']);
+ const classes = Array.from(frame.querySelectorAll('[class]')).flatMap((n) => Array.from(n.classList));
+ expect(classes.filter((c) => c.startsWith('v2p-')).length).toBeGreaterThan(10);
+ expect(frame.querySelectorAll('details.v2p-disc')).toHaveLength(1);
+ });
+
+ test('the Settings frame fills its column to 760 and drops the value cell at 390', () => {
+ render();
+ const frame = screen.getByTestId('v2p-settings-frame');
+ expect(frame.style.width).toBe('100%');
+ expect(frame.style.maxWidth).toBe('760px');
+ expect(css).toMatch(/\.v2p-lrow \{ grid-template-columns: minmax\(0, 1fr\) auto;/);
+ expect(css).toMatch(/\.v2p-frame \.v2p-lrow \.vl[^}]*display: none/);
+ });
});
diff --git a/frontend/src/v2/primitives/V2PrimitivesPage.tsx b/frontend/src/v2/primitives/V2PrimitivesPage.tsx
index 58305b742..60a6a8a90 100644
--- a/frontend/src/v2/primitives/V2PrimitivesPage.tsx
+++ b/frontend/src/v2/primitives/V2PrimitivesPage.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import './primitives.css';
+import V2SettingsFrame from './V2SettingsFrame';
// TASK-730 (D223): the shared primitives rendered alone, so a surface can be
// checked against the artboard frame without the shell around it. Served at
@@ -21,6 +22,9 @@ const V2PrimitivesPage: React.FC = () => (
+
settings frame (d215 s-settings)
+
+
buttons
diff --git a/frontend/src/v2/primitives/V2SettingsFrame.tsx b/frontend/src/v2/primitives/V2SettingsFrame.tsx
new file mode 100644
index 000000000..f76d4f2f5
--- /dev/null
+++ b/frontend/src/v2/primitives/V2SettingsFrame.tsx
@@ -0,0 +1,79 @@
+import React from 'react';
+
+// TASK-730 (D223): the d215 Settings frame (#s-settings) rebuilt from the
+// shared primitives alone. Same groups, rows and order as the artboard; the
+// values are fixtures, so the structural diff is what this proves.
+const seg = (items: string[], on: string, label: string) => (
+