Skip to main content

DHTMLX Kanban. Adaptive columns fit the screen width example

On narrow screens or when the number of columns changes dynamically, fixed-width columns can overflow or leave wasted space. Adaptive columns stretch to fill the available width, ensuring the board always uses the full viewport regardless of screen size.

Live example

const { Kanban, Toolbar, defaultEditorShape } = kanban;

const cardShape = {
    label: true,
    description: true,
    progress: true,
    comments: true,
    votes: {
      show: true,
      clickable: true,
    },
    start_date: true,
    end_date: true,
    users: {
        show: true,
        values: users,
    },
    priority: {
        show: true,
        values: [
            { id: 1, color: "#FF5252", label: "High", value: 1 },
            { id: 2, color: "#FFC975", label: "Medium", value: 2 },
            { id: 3, color: "#65D3B3", label: "Low", value: 3 },
        ],
    },
    color: true,
    menu: true,
    cover: true,
    attached: false,
};

const board = new Kanban("#root", {
    columns,
    cards,
    rows,
    rowKey: "type",
    cardShape,
    editorShape: [
        ...defaultEditorShape,
        {
            type: "comments",
            key: "comments",
            label: "Comments",
            config: {
                placement: "editor",
            },
        },
    ],
    currentUser: 1,
});

new kanban.Toolbar("#toolbar", {
    api: board.api,
});
<!-- styles for adaptive layout -->

<style>
#root .wx-header {
        width: 100%;
    }
    #root .wx-column {
        /* min width */
        --wx-kanban-column-width: 150px;
        /* auto size */
        flex-grow: 1;
    }
    #root .wx-image img {
        object-fit: cover;
    }

	#root .wx-card {
		container-type: inline-size;
		container-name: card;
	}

    #root .wx-card .wx-field.wx-image {
            height: 50cqw;
    }
	@container card (width < 250px) {
		.wx-footer .wx-users {
			display: none;
		}
	}
	@container card (width < 200px) {
		.wx-content .wx-footer {
			display: none;
		}
        .wx-body .wx-field {
            margin-bottom: 0 !important;
        }
        .wx-field > .wx-layout {
            display: none;
        }
	}

	#root .wx-kanban {
		container-type: inline-size;
		container-name: kanban;
	}
	@container kanban (width < 630px) {
		.wx-kanban .wx-sidebar-open {
			width: 100cqw;
			min-width: 100cqw;
		}
    }
</style>

<script>
const users = [
		{
			id: 1,
			label: "Steve Smith",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-1.jpg",
		},
		{
			id: 2,
			label: "Aaron Long",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-2.jpg",
		},
		{
			id: 3,
			label: "Angela Allen",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-3.jpg",
		},
		{
			id: 4,
			label: "Angela Long",
			avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-4.jpg",
		},
		{
			id: 5,
			label: "John Doe",
		},
	];
    
    const editorShape = [
		{
			type: "multiselect",
			key: "users",
			label: "Users",
			options: users,
		},
	];
    
    const columns = [
		{
			label: "Backlog",
			id: "backlog",
		},
		{
			label: "In progress",
			id: "inprogress",
		},
		{
			label: "Testing",
			id: "testing",
		},
		{
			label: "Done",
			id: "done",
		},
	];
    
    const rows = [
		{
			label: "Feature",
			id: "feature",
		},
		{
			label: "Task",
			id: "task",
		},
	];
    
    const cards = [
		{
			label: "Integration with Angular/React",
			priority: 1,
			color: "#65D3B3",
			start_date: new Date("01/07/2021"),
			users: [3, 2],
			column: "backlog",
			type: "feature",
            votes: [1],
            comments:[
                {
					id: 1,
					userId: 1,
					cardId: 1,
					text: "I look forward to seeing you at the integration meeting.",
					date: new Date(),
				},
            ]
		},
		{
			label: "Archive the cards/boards ",
			priority: 3,
			color: "#58C3FE",
			users: [4],
			progress: 1,
			column: "backlog",
			type: "feature",
		},
		{
			label: "Searching and filtering",
			priority: 1,
			color: "#58C3FE",
			start_date: new Date("01/09/2021"),
			users: [3, 1],
			progress: 1,
			column: "backlog",
			type: "task",
		},
		{
			label: "Set the tasks priorities",
			color: "#FFC975",
			start_date: new Date("12/21/2020"),
			users: [4],
			progress: 75,
			column: "inprogress",
			type: "feature",
			attached: [
				{
					isCover: true,
					coverURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-1.jpg",
					previewURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-1.jpg",
					url: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-1.jpg",
					name: "img-1.jpg",
				},
			],
            votes: [2]
		},
		{
			label: "Custom icons",
			color: "#65D3B3",
			start_date: new Date("01/07/2021"),
			users: [3, 2],
			column: "inprogress",
			type: "task",
		},
		{
			label: "Integration with Gantt",
			color: "#FFC975",
			start_date: new Date("12/21/2020"),
			users: [4],
			progress: 75,
			column: "inprogress",
			type: "task",
		},
		{
			label: "Drag and drop",
			priority: 1,
			color: "#58C3FE",
			users: [3, 1],
			progress: 100,
			column: "testing",
			type: "feature",
		},
		{
			label: "Adding images",
			color: "#58C3FE",
			users: [4],
			column: "testing",
			type: "task",
			attached: [
				{
					isCover: true,
					coverURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-2.jpg",
					previewURL: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-2.jpg",
					url: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/img-2.jpg",
					name: "img-2.jpg",
				},
			],
		},
		{
			label: "Create cards and lists from the UI and from code",
			priority: 3,
			color: "#65D3B3",
			start_date: new Date("01/07/2021"),
			users: [3, 2],
			column: "done",
			type: "feature",
		},
		{
			label: "Draw swimlanes",
			color: "#FFC975",
			users: [2],
			column: "done",
			type: "feature",
		},
		{
			label: "Progress bar",
			priority: 1,
			color: "#FFC975",
			start_date: new Date("12/9/2020"),
			users: [1, 4, 3],
			progress: 100,
			column: "done",
			type: "task",
		},
	];
</script>

This demo achieves responsive columns entirely through CSS - no JavaScript config needed. The key technique is setting flex-grow: 1 on .wx-column elements with a --wx-kanban-column-width minimum. CSS container queries (@container) progressively hide card details (user avatars, footers) as columns shrink below certain thresholds. The editor also adapts: on narrow boards (< 630px), it expands to full container width.

Solution overview

  1. Set .wx-column { flex-grow: 1; --wx-kanban-column-width: 150px } to make columns fill available space
  2. Set .wx-header { width: 100% } to stretch column headers
  3. Use @container card (width < 250px) to hide user avatars on narrow cards
  4. Use @container card (width < 200px) to hide the entire footer on very narrow cards
  5. Use @container kanban (width < 630px) to make the editor full-width on small screens

Key points

  • Pure CSS solution: No Kanban API properties needed - all adaptiveness comes from CSS flex-grow and container queries
  • Container queries, not media queries: @container responds to the component's own width, not the viewport - essential for embedded boards
  • Progressive degradation: Card details hide in stages as space shrinks, keeping the label always visible
  • Cover image scaling: height: 50cqw on cover images makes them scale proportionally with column width

API reference

  • cardShape: Configures which card fields are visible

Additional resources