Three Columns
3 editable content columns with responsive stacking and adjustable proportions.
Playground and Examples use the shared browser runtime. For Service Studio inputs and event wiring, use Implementation and API.
Open OutSystems showcase ↗Implement Three Columns in your application
Use the same setup, screen state and event mapping workflow as the existing components. These components are included in Community 1.1.0.
3 editable content columns with responsive stacking and adjustable proportions. These components are included in @blukavantui/community 1.1.0.
Prepare your local Web project
Use a TypeScript application with a CSS-capable bundler. Install @blukavantui/community@1.1.0 from npm, or install the matching downloaded archive.
// In your test application’s package.json dependencies:
"@blukavantui/community": "1.1.0"Run npm install in that test application to install the component package. Load the theme once and the component stylesheet shown in the adapter. Create an empty host in your view:
<div id="example"></div>Mount one typed instance
This reusable adapter follows the Button and TimePicker guide pattern. Its types come from the factory, so required options stay required. Attach the host first; mount once per view and retain the handle.
import { createColumns3 } from '@blukavantui/community/columns3'
import '@blukavantui/community/theme.css'
import '@blukavantui/community/columns3.css'
type Options = NonNullable<Parameters<typeof createColumns3>[1]>
export function mount(host: HTMLElement, options: Options) {
const instance = createColumns3(host, options)
return { instance, update: (patch: Partial<Options>) => instance.update(patch), destroy: () => instance.destroy() }
}
Connect state and updates
Columns owns layout and stable content slots. Your application owns the children: keep their instance handles, update layout in place, and destroy nested components before destroying the layout. There are no component callbacks.
Use one outer card wrapper per column for equal-height content. A ratio or spacing update preserves child nodes and their state.
Keep real content while changing proportions
Use: columns, Ratio, Gap, update
Append existing DOM nodes or child component instances into the returned columns array. update changes layout properties without replacing these slots. Use supported ratios for the chosen column count.
Type into a nested input, then change spacing/proportions and confirm the same input retains its value.
Match row heights and stack naturally
Use: EqualHeight, Align, StackAt
Wrap a card’s content in one outer element per column. EqualHeight stretches that wrapper to the row height and overrides Align. At the selected viewport breakpoint, each stacked row keeps natural height.
Compare short/long cards, enable equal height, then test below the stacking breakpoint.
Dispose children before their layout
Use: destroy
Keep handles for nested components and destroy them before destroying Columns. Removing a parent DOM node alone does not unregister a child component’s global listeners.
Unmount/remount the view and check there is one input instance with no duplicate callbacks.
Use the complete working example
Save this as columns3.ts, import its mount function and pass your attached host. It includes application state, interactive controls and a destroy handler.
Download the editable TypeScript example ↓
Complete Columns3 example
import { createColumns3 } from '@blukavantui/community/columns3'
import { createTextInput } from '@blukavantui/community/text-input'
import '@blukavantui/community/text-input.css'
import '@blukavantui/community/theme.css'
import '@blukavantui/community/columns3.css'
export function mount(host: HTMLElement) {
const root = document.createElement('section')
const toggle = document.createElement('button')
toggle.type = 'button'; toggle.textContent = 'Use natural heights'
const spacing = document.createElement('button')
spacing.type = 'button'; spacing.textContent = 'Change layout spacing'
const target = document.createElement('div')
root.append(toggle, spacing, target); host.append(root)
let equal = true, compact = false
const instance = createColumns3(target, {
Ratio: 'Equal', EqualHeight: equal, Gap: 24, StackAt: 'Medium',
})
const cards = instance.columns.map((column, index) => {
const card = document.createElement('article')
card.style.cssText = 'padding:20px;border:1px solid #94a3b8;border-radius:12px;box-sizing:border-box'
const title = document.createElement('h4'); title.textContent = `Panel ${index + 1}`
const text = document.createElement('p')
text.textContent = index === 0 ? 'A short summary.' :
'A longer panel with instructions, supporting context and follow-up details. It shares the row height when equal height is enabled.'
card.append(title, text); column.append(card); return card
})
const input = createTextInput(cards[0], { Label: 'Notes retained during layout changes', Value: 'Keep this draft' })
const listeners = new AbortController()
toggle.addEventListener('click', () => {
equal = !equal; instance.update({ EqualHeight: equal })
toggle.textContent = equal ? 'Use natural heights' : 'Use equal heights'
}, { signal: listeners.signal })
spacing.addEventListener('click', () => {
compact = !compact
instance.update({ Gap: compact ? 8 : 24, Ratio: compact ? 'WideCenter' : 'Equal' })
}, { signal: listeners.signal })
return { instance, input, destroy() {
listeners.abort(); input.destroy(); instance.destroy(); root.remove()
} }
}
Verify behavior and dispose
- Columns preserve the source order of your content. Put interactive elements in that same logical order.
- Test both sides of the stacking breakpoint and confirm entered child values survive layout updates.
- Call the example handle’s
destroy()when the view is removed. Abort your application listeners and requests; destroy children before their Columns instance. - Mount again and confirm there is one instance and one callback per interaction.
Reference the native producer
In Manage Dependencies, select Columns3 from BluKavantUi_Community_Layout, together with the structures and public actions used by its API. Refresh existing references before publishing your consumer. Inherit the shared Community theme in your application's theme.
Place the Block in your screen
ExampleScreen — Screen (your application)
└─ ExistingLayout — referenced Layout Block instance (child of ExampleScreen)
└─ Content — existing slot on ExistingLayout
└─ ComponentRegion — Container (child of that slot)
└─ MainComponent — BluKavantUi_Community_Layout.Columns3 Block instance (child of ComponentRegion)Reuse your screen’s existing Layout and its content slot; do not add a second layout. If your Layout names that slot differently, use its actual content slot. ComponentRegion is a new Container; MainComponent is a referenced Block instance.
Populate these existing slots on MainComponent with your own widgets. Do not create new Placeholder widgets to stand in for a referenced Block's slots.
| Existing slot | Content responsibility |
|---|---|
| Column1 | Place your own widgets inside Column1. Wrap multiple widgets in one Container when it should stretch as one card. |
| Column2 | Place your own widgets inside Column2. Wrap multiple widgets in one Container when it should stretch as one card. |
| Column3 | Place your own widgets inside Column3. Wrap multiple widgets in one Container when it should stretch as one card. |
Create and bind screen state
Create local variables on ExampleScreen for the values your application changes. Use the exact native types below; lists are typed OutSystems Lists, while inputs ending in Json are Text containing the documented JSON. Bind each MainComponent.Input to its matching StateInput local. Constants can instead use the default directly. Fill required data from your Aggregate or Data Action before enabling interaction.
| MainComponent input | Screen local / native type | Initial value | Purpose |
|---|---|---|---|
| Ratio | StateRatio · Text | "Equal" | Equal; Columns2 also accepts OneThirdTwoThirds, TwoThirdsOneThird, OneQuarterThreeQuarters and ThreeQuartersOneQuarter. Columns3 also accepts WideCenter. Columns4 uses Equal. |
| Gap | StateGap · Integer | 24 | Spacing in pixels, from 0 through 96. Native CSS supports whole-number values. |
| Align | StateAlign · Text | "Stretch" | Start, Center, End or Stretch. Aligns the column containers; use EqualHeight to also stretch a single content wrapper. |
| StackAt | StateStackAt · Text | "Medium" | Small stacks at viewport width 479px or below; Medium at 767px; Large at 1023px. Source order is preserved. |
| EqualHeight | StateEqualHeight · Boolean | False | True stretches the columns and a single content wrapper to the tallest content in each row. Overrides Align while enabled. Stacked rows keep their own natural height. |
All native input defaults
| Input | Type | Default | Required |
|---|---|---|---|
| Ratio | Text | "Equal" | No |
| Gap | Integer | 24 | No |
| Align | Text | "Stretch" | No |
| StackAt | Text | "Medium" | No |
| ExtendedClass | Text | "" | No |
| EqualHeight | Boolean | False | No |
| EnableMotion | Boolean | False | No |
| MotionPreference | Text | "System" | No |
| UseDefaultMotionDuration | Boolean | True | No |
| MotionDurationMs | Integer | 450 | No |
| MotionEasing | Text | "standard" | No |
| MotionReplayKey | Text | "" | No |
Create the event handlers and map every argument
For each event you use, create the named screen Client Action below. Add its listed input parameters with the exact types. Select that action on MainComponent's event property, then enter each mapping explicitly. Event arguments and screen locals are different scopes.
This Block has no events to wire. The consuming screen controls its values and child content.
Set proportions and equal height
Place each column’s content in its existing Column slot. Set EqualHeight = True to fill the row height. Put a card’s contents inside one Container per slot so that wrapper stretches with the column. No fixed content height is required; stacked rows retain natural height.
Align controls vertical placement when EqualHeight is False; EqualHeight overrides it with Stretch when True. For nested layouts, enable EqualHeight separately on each row that needs it.
Fixed proportion helper Blocks show their widths in Service Studio. Dynamic Ratio, Gap, Align, EqualHeight and StackAt input values resolve at runtime; the designer displays structural defaults. Compiled acceptance covers all eight presets and equal-height rows, with a narrow Safari stacking check.
MainComponent.EqualHeight = True
MainComponent.Gap = 24
MainComponent.StackAt = "Medium"
MainComponent.Column1 → CardOne Container → your widgets
MainComponent.Column2 → CardTwo Container → your widgetsUse the related designer Blocks
Columns3WideCenter
Producer: BluKavantUi_Community_Layout. Existing placeholders: Column1, Column2, Column3.
| Input | Native type / default | Usage |
|---|---|---|
| Gap | Integer · 24 | Spacing in pixels, from 0 through 96. Native CSS supports whole-number values. |
| Align | Text · "Stretch" | Start, Center, End or Stretch. Aligns the column containers; use EqualHeight to also stretch a single content wrapper. |
| StackAt | Text · "Medium" | Small stacks at viewport width 479px or below; Medium at 767px; Large at 1023px. Source order is preserved. |
| ExtendedClass | Text · "" | Additional CSS classes on the component wrapper. Keep custom styles scoped. |
| EqualHeight | Boolean · False | True stretches the columns and a single content wrapper to the tallest content in each row. Overrides Align while enabled. Stacked rows keep their own natural height. |
Verify in your consumer
Publish the consumer, supply representative data, trigger each connected event and inspect the mapped arguments. Check a failed request, a retry and leaving/reopening the screen. The table describes the native contract; the Web recipes can contain capabilities with different native names or availability. Use these native names and the API tab as the authority for Service Studio.
Columns3 with application state
These components are included in @blukavantui/community 1.1.0. This is the same complete example used by the implementation guide, with real controls, event handling and cleanup.
import { createColumns3 } from '@blukavantui/community/columns3'
import { createTextInput } from '@blukavantui/community/text-input'
import '@blukavantui/community/text-input.css'
import '@blukavantui/community/theme.css'
import '@blukavantui/community/columns3.css'
export function mount(host: HTMLElement) {
const root = document.createElement('section')
const toggle = document.createElement('button')
toggle.type = 'button'; toggle.textContent = 'Use natural heights'
const spacing = document.createElement('button')
spacing.type = 'button'; spacing.textContent = 'Change layout spacing'
const target = document.createElement('div')
root.append(toggle, spacing, target); host.append(root)
let equal = true, compact = false
const instance = createColumns3(target, {
Ratio: 'Equal', EqualHeight: equal, Gap: 24, StackAt: 'Medium',
})
const cards = instance.columns.map((column, index) => {
const card = document.createElement('article')
card.style.cssText = 'padding:20px;border:1px solid #94a3b8;border-radius:12px;box-sizing:border-box'
const title = document.createElement('h4'); title.textContent = `Panel ${index + 1}`
const text = document.createElement('p')
text.textContent = index === 0 ? 'A short summary.' :
'A longer panel with instructions, supporting context and follow-up details. It shares the row height when equal height is enabled.'
card.append(title, text); column.append(card); return card
})
const input = createTextInput(cards[0], { Label: 'Notes retained during layout changes', Value: 'Keep this draft' })
const listeners = new AbortController()
toggle.addEventListener('click', () => {
equal = !equal; instance.update({ EqualHeight: equal })
toggle.textContent = equal ? 'Use natural heights' : 'Use equal heights'
}, { signal: listeners.signal })
spacing.addEventListener('click', () => {
compact = !compact
instance.update({ Gap: compact ? 8 : 24, Ratio: compact ? 'WideCenter' : 'Equal' })
}, { signal: listeners.signal })
return { instance, input, destroy() {
listeners.abort(); input.destroy(); instance.destroy(); root.remove()
} }
}
Mount and dispose
Call mount after the host is attached to the document. Retain the returned handle and call destroy from your view’s unmount hook.
import { mount } from './columns3'
const host = document.getElementById('example')
if (!host) throw new Error('Missing example host')
const view = mount(host)
// Register this with your framework/router’s unmount hook:
function disposeView() { view.destroy() }Use instance.update(patch) for changes while mounted; creating another instance on each render leaves duplicate controls.
Build with native Blocks
Configure the referenced Block in Service Studio using typed inputs, screen state and Client Actions. The Implementation tab includes the widget tree and exact event argument mappings.
createColumns3
Columns owns layout and stable content slots. Your application owns the children: keep their instance handles, update layout in place, and destroy nested components before destroying the layout. There are no component callbacks.
import { createColumns3 } from '@blukavantui/community/columns3';
import '@blukavantui/community/columns3.css';Load @blukavantui/community/theme.css once in your app shell.createColumns3(host: HTMLElement, options?: ColumnsOptions)These components are included in @blukavantui/community 1.1.0. Required/optional and signatures come from the 1.1.0 TypeScript source. Defaults below describe runtime behavior, which may differ from the Playground’s selected example.
Options
6| Name | Type / requirement | Usage |
|---|---|---|
Ratio | "Equal" | "OneThirdTwoThirds" | "TwoThirdsOneThird" | "OneQuarterThreeQuarters" | "ThreeQuartersOneQuarter" | "WideCenter" | undefinedOptional | Default: "Equal". Equal or WideCenter (1:2:1). |
EqualHeight | boolean | undefinedOptional | Default: false. Stretches a single content wrapper in every column to the tallest content within that row. Overrides Align while true. Stacked rows keep natural heights. |
Gap | number | undefinedOptional | Default: 24. Spacing in pixels, bounded from 0 to 96. |
Align | "Start" | "Center" | "End" | "Stretch" | undefinedOptional | Default: "Stretch". Start, Center, End or Stretch. Aligns column containers; EqualHeight also stretches their single content wrapper. |
StackAt | "Small" | "Medium" | "Large" | undefinedOptional | Default: "Medium". Viewport breakpoint: Small ≤479px, Medium ≤767px, Large ≤1023px. Columns stack in source order; this is not a container-width query. |
ExtendedClass | string | undefinedOptional | Default: "". Additional space-separated classes on the root. Keep application CSS scoped. |
Callbacks
No component callbacks. Connect behavior to your application-owned content.
Instance methods & properties
4| Name | Signature / type | Usage |
|---|---|---|
element | HTMLElement | Root DOM element owned by the component. Mount into a real host before using focus or validation. |
columns | HTMLElement[] | Stable array of real content elements, in source order. Append your own widgets. Updating proportions keeps these elements and their children. |
update | (patch: Partial<ColumnsOptions>) => void | Shallow-merge an options patch into the current configuration. Arrays/adornment objects replace their previous value. User-change callbacks are not emitted. Call before destroy; updates afterward throw. |
destroy | () => void | Remove owned DOM and listeners. Destroy child component instances before their layout. Safe to call repeatedly; do not use other methods afterward. |
Related data types
Expand a type for its exact fields and allowed values. Pass objects and arrays directly in Web code.
ColumnsOptions
Current local TypeScript declaration. Use the component-specific options and callbacks shown above.
interface ColumnsOptions {
Ratio?: 'Equal' | 'OneThirdTwoThirds' | 'TwoThirdsOneThird' | 'OneQuarterThreeQuarters' | 'ThreeQuartersOneQuarter' | 'WideCenter';
/** Match column heights within a row and stretch a single content wrapper. Stacked rows keep natural height. */
EqualHeight?: boolean;
Gap?: number; Align?: 'Start' | 'Center' | 'End' | 'Stretch';
StackAt?: 'Small' | 'Medium' | 'Large'; ExtendedClass?: string;
}ColumnsComponent
Current local TypeScript declaration. Use the component-specific options and callbacks shown above.
interface ColumnsComponent { element: HTMLElement; columns: HTMLElement[]; update(patch: Partial<ColumnsOptions>): void; destroy(): void; }Verified native contract for BluKavantUi_Community_Layout → Essentials → Columns3. Bindings, events and layout were verified in the compiled development consumer on 2026-10-24.
Browser checks cover labels, keyboard interactions, values and cleanup. Scoped Safari VoiceOver and browser checks passed; this is not a full-library conformance certification. Test your composed application with its actual content and assistive technologies.
Purpose and use
3 editable content columns with responsive stacking and adjustable proportions.
Keyboard design guidance
Columns preserve the source order of your content. Put interactive elements in that same logical order.
See the support status above for review coverage and any limitations.
In your application
Provide useful labels, preserve visible focus, and test the component with your actual content. Use text alongside status colors and check contrast in each state.
Events0No events yet.
Interact with an example to see its events.