Skip to content

CSS Container Queries, what are they?

CSS Container Queries, what are they?

CSS Container queries, what are they?

Intro

Media queries have always been crucial to building web applications. They help make our apps more accessible and easier to use and ensure we reach most of our audience. Media queries have been essential in frontend development to create unique user interfaces.

But now, there’s something new: Container queries.

In this blog post, we’ll explore what Container queries are, how they differ from media queries, and why they’re so amazing.

So, let’s get started!

Refresh on Media queries

Media queries have been available in browsers for a long time, but they didn’t become popular until around 2010 when mobile devices started to take off.

Media queries let us add specific styles based on the type of device, like screens or printers. This is especially helpful for creating modern, responsive apps.

A simple use of Media queries would be changing, for example, a paragraph's font size when the screen width is less than a specific number.

p {
  font-size: 12px
}

// Media query
@media screen and (min-width: 400px) {
 p {
   font-size: 8px
 }
}

In this simple example, when the browser’s viewport width is less or equal to 400px, the font size changes to 8px.

Notice how straightforward the syntax is: we start with the keyword @media, followed by the type of device it should apply to. In this case, we use screen so it doesn’t affect users who print the page—if you don’t add anything, then it falls back to the default, which is “all” including both print and screen. Then we specify a media feature, in this case, the width.

Container queries

Container queries are similar to Media queries. Their main function is to apply styles under certain conditions. The difference is that instead of listening to the viewport of the browser, it listens to a container size. Let’s see this example:

dashboard screenshot 1

In the above layout, we have a layout with a sidebar and three cards as the content. Using Media queries we could listen to the viewport width and change the layout depending on a specific width. Like so:

@media (max-width: 768px) {
  .layout {
    flex-direction: column;
  }

  .sidebar {
    width: 100%;
    border-right: none;
    border-bottom: 1px solid #333;
  }

  .card-inner {
    flex-direction: column;
  }

  .card-left {
    border-right: none;
    border-bottom: 1px solid #333;
  }
}

dashboard screenshot 2

That’s acceptable, but it requires us to constantly monitor the layout. For example, if we added another sidebar on the right (really weird, but let’s imagine that this is a typical case), our layout would become more condensed:

dashboard screenshot 3

We would need to change our media queries and adjust their range in this situation. Wouldn’t it be better to check the card container’s width and update its styles based on that? That way, we wouldn’t need to worry about if the layout changes, and that’s precisely what container queries are made for!

First, to define the container we are going to listen to, we are going to add a new property to our styles:

// cards container
.container {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  justify-content: flex-start;
  // new property to define our container
  container-type: inline-size;
}

The .container class is the one in which our cards reside. By adding the property `container-type, ' we now define this class as a container we want to listen to. We said inline-size as the value to query based on the inline dimensions of the container because we just want to listen to the element's width.

The value of container-type will depend on your use case. If you want to listen to both width and height, then size will be a better fit for you.

You can also have normal as your container-type value, which means the element won’t act as a query container at all. This is handy if you need to revert to the default behavior.

Next, to define our query, we use the new @container CSS at-rule:

@container (max-width: 400px) {
  .card-inner {
    flex-direction: column;
  }

  .card-left {
    border-right: none;
    border-bottom: 1px solid #333;
  }
}

Notice that it is really similar to how we define our Media queries. Now, if we look at the same screen, we will see the following:

dashboard screenshot 4

This is very powerful because we can now style each component with its own rules without changing the rules based on the layout changes.

The @container will affect all the defined containers in the scope; we might not want that. We can define the name of our container to specify that we only want to listen to that in specific:

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  justify-content: flex-start;
  container-type: inline-size;
  // New property to define the name of our container
  container-name: cards-container;
}

//We now specify which container we are listening to
@container cards-container (max-width: 400px) {
  .card-inner {
    flex-direction: column;
  }

  .card-left {
    border-right: none;
    border-bottom: 1px solid #333;
  }
}

We can also have a shorthand to define our container and its name:

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  justify-content: flex-start;
  // name of our container / its type
  container: cards-container / inline-size;
}

Container query length units

Container query lengths are similar to the viewport-percentage length units like vh or vw units, but instead of being relative to the viewport, they are to the dimensions of the query container. We have different units, each relative to different dimensions of the container:

  • cqw: 1% of a query container's width
  • cqh: 1% of a query container's height
  • cqi: 1% of a query container's inline size
  • cqb: 1% of a query container's block size
  • cqmin: The smaller value of either cqi or cqb
  • cqmax: The larger value of either cqi or cqb

In our example, we could use them to define the font size of our cards:

.card p {
  // Pick the maximum value.
  font-size: max(16px, 1cqi);
}

Using these units alone isn’t recommended because they’re percentage-based and can have a value we don’t want. Instead, it’s better to use a dynamic range. Using the max function, we can set 2 values and always pick the highest one.

Conclusion

Container queries bring a fresh and powerful approach to web design but are not meant to replace Media queries. I think their real power shines when used together.

Media queries often require constant adjustments as your layout evolves. Container queries, however, let you style individual components based on their dimensions, making the designs more flexible and easier to manage.

Adding a new component or rearranging elements won’t force us to rewrite our media queries. Instead, each component handles its styling, leading to cleaner and more organized code.

Please note that, as of writing this blog post, they aren’t compatible with all browsers yet. Take a look at this table from caniuse.com:

can I use css container style queries

A good fallback strategy for this, when hitting an unsupported browser would be the use of the @support rule, which allows you to apply styles only if the browser supports the CSS feature. For example:

/* Fallback for browsers that don't support container queries */
@supports not (container-type: inline-size) {
  @media screen and (max-width: 1024px) {
    .card-inner {
      flex-direction: column;
    }

    .card-left {
      border-right: none;
      border-bottom: 1px solid #333;
    }
  }
}

@container cards-container (max-width: 400px) {
  .card-inner {
    flex-direction: column;
  }

  .card-left {
    border-right: none;
    border-bottom: 1px solid #333;
  }
}

Ensure your media queries are good enough to keep everything responsive and user-friendly when the condition is unmet.

Thank you for reading! Enjoy the extra flexibility that container queries bring to your web designs. Check out a live demo to see it in action. Happy styling!

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

:where functional pseudo-selectors :is valuable in CSS cover image

:where functional pseudo-selectors :is valuable in CSS

If you’ve written CSS before, you’ve used pseudo selectors. Typically, we’d use them to style :hover events or select every :nth-of-type() In recent years CSS has become a lot more powerful, and with that, we now have many more pseudo-selectors available to use. Let’s explore some functional pseudo-selectors together and see how to use them to enhance our front-end code. Functional pseudo-classes While there are a wide range of pseudo-classes, I want to focus on the _functional_ ones today. :is :is works very similar to a regular CSS class list at first glance ` One of its main benefits is that you can group CSS classes to form more readable and maintainable conditions. ` For deep nesting, this can make your CSS significantly easier to understand, simplifying editing at a later date. ` You may be thinking that CSS has another solution that improves readability in a similar way: nesting. That’s true, and in this context, you can use them interchangeably, though the syntax is still shorter. ` However deeper nesting using nested CSS can become complex quickly. Sometimes it makes sense to use :is to help avoid this complexity. ` :is also provides a specificity modifier you don’t get with nested css. If you write: ` Every selector within the:is would be treated as if it had the same specificity value as the ID. With :is, the highest value applies to all other values within the :is. This does not apply to nested CSS. :not It does exactly what you think it does. The :not selector grabs everything _except_ the classes you define: ` Like the other pseudo-classes, you can negate multiple classes if you choose: ` This can be powerful when combined with other pseudo-selectors: ` :where Using the :where pseudo-class is great for creating low-specificity CSS. If you create a library or plugin that’s going to be used by other people and you want them to style it themselves, but don’t want it to appear ugly when they first set it up, :where is the way to do that. For example, let’s style all our links with an underline using :where ` With this approach, we can override our link’s default styles without having to create difficult to maintain CSS: ` If we didn't use :where above, import order would matter if we wanted to override this. But because we planned ahead, we can just use a standard a tag. Without using :where we’re stuck with options that are much harder to work with: ` :where also has the same benefits of class grouping that :is does but without the specificity, so you can do something like this and then override it easily later. ` :has One of the most powerful pseudo selectors is :has, which gives you the option to style a tag or class based on other classes or tags associated with it. ` An incredible benefit :has brings is the ability to create parent selector functionality. This gives you a wide variety of options to style the parent based on the state of the children. ` You can also combine this with the :not selector to select elements that don’t have specific children. ` Benefits of Pseudo-Classes Readability One of the benefits of all of these pseudo-selectors is that they can act similarly to nested CSS. It’s possible to use these to make your code more readable. ` Using :is this way is effectively the same as using nested CSS, but if you need lower specificity you could use :where, or :not if you want to exclude (instead of include) some classes. Functionality :not and :has provide new options which weren’t possible before, allowing you to style more dynamically and provide better experiences while simplifying your code. Before these options were available, the only solution was to style the code using JavaScript. While this technically allows you to achieve your styling goals, it’s not ideal. Mixing CSS operations into JS files makes it much harder to maintain long-term because it adds a layer of abstraction to your solution, while the built-in CSS option is much simpler. While :is and :where don’t provide as much new functionality, they still allow you to write more understandable CSS with less ambiguity or workarounds, making maintenance significantly easier. Summing up Modern CSS allows us to be much more flexible with our styles, all while writing less code. Simplifying CSS and removing the need to compensate, either by writing extra CSS or styling through JS, means our CSS files are more explicit and easier to maintain in the long term. They may not be needed often, but they’re an incredibly important part of your front-end toolbelt. Have you found any interesting ways to use functional pseudo-classes? Send us a post on X or message us on LinkedIn and show me what cool things you’ve made....

Exploring Open Props and its Capabilities cover image

Exploring Open Props and its Capabilities

Exploring Open Props and its Capabilities With its intuitive approach and versatile features, Open Props empowers you to create stunning designs easily. It has the perfect balance between simplicity and power. Whether you're a seasoned developer or just starting, Open Props makes styling websites a breeze. Let's explore how Open Props can help your web development workflow. What is Open Props Open Props is a CSS library that packs a set of CSS variables for quickly creating consistent components using “Sub-Atomic” Styles. These web design tokens are crafted to help you get great-looking results from the start using consistent naming conventions and providing lots of possibilities out-of-the-box. At the same time, it's customizable and can be gradually adopted. Installing open props There are many ways to get started with open props, each with its advantages. The library can be imported from a CDN or installed using npm. You can import all or specific parts of it, and for greater control of what's bundled or not, you can use PostCSS to include only the variables you used. From Zero to Open Props Let's start with the simplest way to test and use open props. I'll create a simple HTML file with some structure, and we'll start from there. Create an index.html file. ` Edit the content of your HTML file. In this example, we’ll create a landing page containing a few parts: a hero section, a section for describing the features of a service, a section for the different pricing options available and finally a section with a call to action. We’ll start just declaring the document structure. Next we’ll add some styles and finally we’ll switch to using open props variables. ` To serve our page, we could just open the file, but I prefer to use serve, which is much more versatile. To see the contents of our file, let's serve our content. ` This command will start serving our site on port 3000. Our site will look something like this: Open-props core does not contain any CSS reset. After all, it’s just a set of CSS variables. This is a good start regarding the document structure. Adding open props via CDN Let's add open-props to our project. To get started, add: ` This import will make the library's props available for us to use. This is a set of CSS variables. It contains variables for fonts, colors, sizes, and many more. Here is an excerpt of the content of the imported file: ` The :where pseudo-class wraps all the CSS variables declarations, giving them the least specificity. That means you can always override them with ease. This imported file is all you need to start using open props. It will provide a sensible set of variables that give you some constraints in terms of what values you can use, a palette of colors, etc. Because this is just CSS, you can opt-out by not using the variables provided. I like these constraints because they can help with consistency and allow me to focus on other things. At the same time, you can extend this by creating your own CSS variables or just using any value whenever you want to do something different or if the exact value you want is not there. We should include some styles to add a visual hierarchy to our document. Working with CSS variables Let's create a new file to hold our styles. ` And add some styles to it. We will be setting a size hierarchy to headings, using open props font-size variables. Additionally, gaps and margins will use the size variables. ` We can explore these variables further using open-props’ documentation. It's simple to navigate (single page), and consistent naming makes it easy to learn them. Trying different values sometimes involves changing the number at the end of the variable name. For example: font-size-X, where X ranges from 0 to 8 (plus an additional 00 value). Mapped to font-sizes from 0.5rem up to 3.5rem. If you find your font is too small, you can add 1 to it, until you find the right size. Colors range from 0-12: –red-0 is the lightest one (rgb(255, 245, 245)) while –red-12 is the darkest (rgb(125, 26, 26)). There are similar ranges for many properties like font weight, size (useful for padding and margins), line height and shadows, to name a few. Explore and find what best fits your needs. Now, we need to include these styles on our page. ` Our page looks better now. We could keep adding more styles, but we'll take a shortcut and add some defaults with Open Props' built in normalized CSS file. Besides the core of open props (that contains the variables) there’s an optional normalization file that we can use. Let's tweak our recently added styles.css file a bit. Let’s remove the rules for headings. Our resulting css will now look like this. ` And add a new import from open-props. ` Open props provides a normalization file for our CSS, which we have included. This will establish a nice-looking baseline for our styles. Additionally, it will handle light/dark mode based on your preferences. I have dark mode set and the result already looks a lot better. Some font styles and sizes have been set, and much more. More CSS Variables Let's add more styles to our page to explore the variables further. I'd like to give the pricing options a card style. Open Props has a section on borders and shadows that we can use for this purpose. I would also like to add a hover effect to these cards. Also, regarding spacing, I want to add more margins and padding when appropriate. ` With so little CSS added and using many open props variables for sizes, borders, shadows, and easing curves, we can quickly have a better-looking site. Optimizing when using the CDN Open props is a pretty light package; however, using the CDN will add more CSS than you'll probably use. Importing individual parts of these props according to their utility is possible. For example, import just the gradients. ` Or even a subset of colors ` These are some options to reduce the size of your app if using the CDN. Open Props with NPM Open Props is framework agnostic. I want my site to use Vite. Vite is used by many frameworks nowadays and is perfect to show you the next examples and optimizations. ` Let's add a script to our package.json file to start our development server. ` Now, we can start our application on port 5173 (default) by running the following command: ` Your application should be the same as before, but we will change how we import open props. Stop the application and remove the open-props and normalize imports from index.html. Now in your terminal install the open-props package from npm. ` Once installed, import the props and the normalization files at the beginning of your styles.css file. ` Restart your development server, and you should see the same results. Optimizing when using NPM Let's analyze the size of our package. 34.4 kb seems a bit much, but note that this is uncompressed. When compressed with gzip, it's closer to 9 kb. Similar to what we did when using the CDN, we can add individual sections of the package. For example in our CSS file we could import open-props/animations or open-props/sizes. If this concerns you, don't worry; we can do much better. JIT Props To optimize our bundled styles, we can use a PostCSS plugin called posts-jit-props. This package will ensure that we only ship the props that we are using. Vite has support for PostCSS, so setting it up is straightforward. Let's install the plugin: ` After the installation finishes, let's create a configuration file to include it. ` The content of your file should look like this: ` Finally, remove the open-props/style import from styles.css. Remember that this file contains the CSS variables we will add "just in time". Our page should still look the same, but if we analyze the size of our styles.css file again, we can see that it has already been reduced to 13.2kb. If you want to know where this size is coming from, the answer is that Open Props is adding all the variables used in the normalize file + the ones that we require in our file. If we were to remove the normalize import, we would end up with much smaller CSS files, and the number of props added just in time would be minimal. Try removing commenting it out (the open-props/normalize import) from the styles.css file. The page will look different, but it will be useful to show how just the props used are added. 2.4kB uncompressed. That's a lot less for our example. If we take a quick look at our generated file, we can see the small list of CSS variables added from open props at the top of our file (those that we use later on the file). Open props ships with tons of variables for: - Colors - Gradients - Shadows - Aspect Ratios - Typography - Easing - Animations - Sizes - Borders - Z-Index - Media Queries - Masks You probably won't use all of these but it's hard to tell what you'll be using from the beginning of a project. To keep things light, add what you need as you go, or let JIT handle it for you. Conclusion Open props has much to offer and can help speed your project by leveraging some decisions upfront and providing a sensible set of predefined CSS Variables. We've learned how to install it (or not) using different methods and showcased how simple it is to use. Give it a try!...

A Deep Dive into SvelteKit's Rendering Techniques cover image

A Deep Dive into SvelteKit's Rendering Techniques

A Deep Dive into SvelteKit's Rendering Techniques Introduction SvelteKit is a meta-framework for Svelte that allows you to develop pages based on their content. At its core, SvelteKit introduces three fundamental strategies out of the box, each designed to streamline the development process and adapt to the specific needs of your project. These strategies enable you to easily create dynamic, responsive, and highly interactive web applications. These strategies, or as SvelteKit calls them, page options, are: * Prerender: Ideal for static content that doesn't change, making your pages lightning-fast to load. * SSR (Server-Side Rendering): Ideal for rendering full pages with dynamic content from a server. * CSR (Client-Side Rendering): Best for highly dynamic and interactive applications where content updates frequently based on user actions. These page options can be applied to specific pages (when exported from **+page.js or +page.server.js) or to a group of pages (when exported from +layout.js or +layout.server.js). They can also be set across the entire application. You accomplish this by exporting it from the root layout. It's worth noting that child layouts and pages can supersede settings from parent layouts. This means you could activate prerendering for the whole app and then turn it off for certain pages that require dynamic server rendering (SSR). In this blog post, we'll explore these page options and share some insights on how you might use them in everyday web projects. Prerendering Prerendering is akin to taking a snapshot of your web pages when you build your application; you might have heard of this as static rendering. This snapshot is then served to all users, ensuring lightning-fast load times since the server simply delivers the pre-built files without additional processing rather than dynamically generating the files for each request. This method is perfect for content that remains unchanged across visits, such as blog posts, documentation, or landing pages. In other words, pages with no dynamic content. There will be situations where you would like to avoid using this option, though. The rule of thumb is that if two different users will see different content, then this is a no-go. Other reasons are inconvenience for your needs. For example, your build times could drag if you end up prerendering tons of pages. Is that convenient to you? Well, that’s for you to decide! The prerender option is turned off by default, so we need to enable it if we want to start using it. Export the following in the +page.server.js or just +page.js: ` Likewise, if you have a group of pages, you could do the same inside a +layout.js or +layout.server.js . If your app is suitable to be all SSG (Static Side Generation), you could use adapter-static, which will output files suitable for use with any static web server. In cases where you happen to opt-in for this, you could turn off pages that need dynamic rendering: ` Another cool feature about prerendering is that pages that fetch data from server routes can automatically inherit default values during the prerendering process. This feature simplifies data management and enhances the development workflow, especially when dealing with dynamic content that needs to be prerendered with specific data sets. Let's say you have a blog where each post fetches its content from a server route when the page loads. Normally, this would require the user's browser to make a request to the server at runtime. However, with prerendering, you can have this data fetched and embedded into the page at build time. ` In this example, when the blog/[slug].sveltepage is prerendered, it makes a fetch call to /api/posts/[slug], which returns the post data. This data is then used to prerender the blog post page with the content already in place, allowing the page to load instantly for the user, with the blog post content visible even before any JavaScript is executed on the client side. There’s a third and useful option when prerendering: ` Using this option is like telling SvelteKit to make a smart guess about whether to prerender your page in advance. You're saying, "Hey SvelteKit, you decide if this page should be prerendered based on what you find about the page" SvelteKit analyzes your page and if it determines the page can be prerendered without complications, it will automatically generate a prerendered version. This process involves SvelteKit either crawling a link inside an already prerendered page, prerendering entry points or targeting pages that are specifically marked for prerendering set in entries. An example is a blog section where you post articles regularly. The blog section has a main page that lists all your blog posts and individual pages for each blog post. The structure for this might be something like: In this setup, the /blog main entry page will be prerendered automatically. However, individual blog posts at paths like /blog/[slug] will not be prerendered unless linked directly from another prerendered page. For instance, if there’s a link to /blog/some-cool-slug, like: <a href="/blog/some-cool-slug">, SvelteKit will be able to crawl that link and prerender that specific page as well. Now let’s say that you would like to prerender your latest blog post, but there's no link for SvelteKit to crawl to this page. In these cases, you can explicitly add these pages to the prerender entries list. By doing so, SvelteKit will prerender every specified entry, ensuring that the content is immediately accessible to users. You can configure the prerender entries directly in your svelte.config.js file or by exporting an entries function from a +page.js, a +page.server.js or a +server.js belonging to a dynamic route. Here’s how you can do it: ` SSR SSR is similar to prerendering. It ensures that pages are first rendered on the server. This process generates the complete HTML content, which is then sent to the client for hydration). This approach enhances the initial page load performance and SEO, providing a better user experience. Unlike prerendering, where pages are generated at build time, SSR pages are created at runtime. This key difference allows for the generation of dynamic content, making SSR particularly suited for applications that require personalized content for each user or real-time updates, such as user dashboards, e-commerce sites, and social media platforms. Similar to prerendering, SSR comes with its own set of limitations, and there are scenarios where it might not be the best choice for your project: * SSR can be expensive. It can significantly load your server, especially for high-traffic sites, as each page request involves rendering content on the server. If server resources are constrained, this could lead to performance bottlenecks. * Highly Interactive Applications: Applications that rely heavily on user interactions and real-time updates (like games or interactive tools like an admin panel) might not benefit much from SSR. CSR can provide a smoother user experience in these cases, as it minimizes server requests after the initial load. * SEO Is Not YOUR Priority: If search engine optimization isn't a key concern for your project (for example, an internal dashboard or an app behind a login), the SEO benefits of SSR might not justify the additional complexity and server demands. export const ssr = true is the option enabled by default. Thus, we can use its benefits from the start. To execute code exclusively on the server, such as fetching data from a database or an external API, SvelteKit offers a convention using +page.server.js files. This setup is ideal for server-side operations, ensuring sensitive logic and credentials are not exposed to the client. ` Note: When you employ +page.js in SvelteKit, the contained code is executed on both the server and client sides by default. However, if you intend for the code to run exclusively on the client side, you can disable SSR by setting export const ssr = false within your +page.js file. Doing so ensures that the code is only executed in the browser, adhering to client-side rendering principles and turning your app into a SPA. This is useful for cases where you don’t need the load on the server, or you don’t benefit from any of the benefits of SSR. CSR CSR plays a crucial role in making web pages interactive by hydrating them and incorporating JavaScript. JavaScript is crucial for adding interactivity to web pages—everything from animations and video players to form validations and dynamic content updates relies on JavaScript. However, there are instances where JavaScript is unnecessary. Consider a prerendered 'About' page; enabling CSR on this page could be excessive, especially if it lacks interactive elements. In such scenarios, you can streamline your page by disabling CSR that comes enabled by default, which can be done by simply doing this: ` Using this trick smartly can make your web pages load fast because downloading JavaScript is sometimes heavy. The key lies in strategically combining this approach with other rendering techniques to optimize performance and user experience. One factor to consider, though, is that turning off CSR also means you'll lose client-side routing. This requires you to depend on traditional browser navigation instead. Conclusion By thoughtfully applying these strategies, you can craft a SvelteKit application that performs exceptionally and delivers a fantastic user experience tailored to your audience's needs. Wrapping up our journey through SSR with SvelteKit. We discovered how choosing the right way to render pages (like prerendering, SSR, or CSR) is super important and can make a big difference in how well a website works. SvelteKit is awesome because it lets you pick the best method for your website. So, remember, playing with SvelteKit and these rendering methods can make your websites stand out. Use it to build websites that not only work great but are also fun and easy for people to use. Dive in, try things out, and see how your web projects can shine!...

What does it actually look like to build software with AI today? Not in theory, but in practice. cover image

What does it actually look like to build software with AI today? Not in theory, but in practice.

What does it actually look like to build software with AI today? Not in theory, but in practice. At the Leadership Exchange, this was the question at the center of the Developer Panel, where leaders from across the industry unpacked what’s really changing inside engineering teams and what organizations need to do right now to keep up. The Developer Panel at the Leadership Exchange explored the cutting edge of AI in software engineering and examined what organizations should focus on today to prepare for the future. Moderated by Jeff Cross, Co-Founder & CEO at Nx, the panel featured Victor Savkin, Cofounder & CTO at Nx, Alex Sover, Vice President of Engineering at OpenAP, Brent Zucker, Senior Director of Engineering at Visa, and Jonathan Fontanez, AI Engineering Lead at This Dot Labs. Panelists shared insights into how AI is transforming the software development lifecycle and how teams can adopt tools effectively while preparing for organizational change. Panelists discussed emerging workflows, including CI-in-the-loop, agentic healing, and context engineering. They examined how validation, code reviews, and PRDs are evolving alongside AI capabilities and how teams are integrating external sources such as production traces to improve quality and reliability. The discussion also covered what the next generation of agentic tools might look like and how these capabilities will shape engineering practices in the near future. Adoption of AI comes with challenges. Teams often rely on plugins or extensions without foundational understanding, and individual contributors may fear displacement. Panelists emphasized that education, governance, and skill-building are essential for teams to manage AI agents effectively while maintaining quality. They also highlighted the need to standardize workflows and ensure organizational alignment to fully leverage AI capabilities. The conversation extended beyond technical challenges to organizational implications. Panelists discussed how teams can avoid issues like Conway’s Law, manage distributed teams effectively, and evolve engineering practices alongside AI adoption. Leadership and management strategies play a crucial role in ensuring that AI integration delivers meaningful outcomes while maintaining efficiency and alignment with business objectives. Key Takeaways - AI workflows require both technical and organizational preparation. - Education, governance, and skill development are essential for successful implementation. - Forward-looking teams are rethinking validation, CI pipelines, and context management to fully leverage agentic AI. The discussion highlighted that adopting AI at the cutting edge is not just about new tools - it is about rethinking processes, workflows, and organizational culture. Companies that embrace this holistic approach are most likely to succeed in leveraging AI to its full potential. Are you interested in more conversations like this? Message us for an invite to the next, or for a private discussion around these topics. Tracy can be reached at tlee@thisdot.co....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co