Repository: siemens/ix
Pull request #1844: docs(icons): refactor icon selection
Author: login: nuke-ellington
Created: 2025-04-17T14:53:57Z | Merged: 2025-04-23T08:41:37Z

<!--
  First off, thanks for taking the time to contribute! ❤️
  We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory.
-->
## 💡 What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

GitHub Issue Number: IX-2491

## 🆕 What is the new behavior?

<!-- Please describe the behavior or changes that are being added by this PR. -->

-
-

## 🏁 Checklist

A pull request can only be merged if all of these conditions are met (where applicable):

- [ ] 🦮 Accessibility (a11y) features were implemented
- [ ] 🗺️ Internationalization (i18n) - no hard coded strings
- [ ] 📲 Responsiveness - components handle viewport changes and content overflow gracefully
- [x] 📄 Documentation was reviewed/updated (`pnpm run docs`)
- [ ] 🧪 Unit tests were added/updated and pass (`pnpm test`)
- [ ] 📸 Visual regression tests were added/updated and pass ([Guide](https://github.com/siemens/ix/blob/main/CONTRIBUTING.md#visual-regression-testing))
- [x] 🧐 Static code analysis passes (`pnpm lint`)
- [x] 🏗️ Successful compilation (`pnpm build`, changes pushed)

## 👨‍💻 Help & support

<!-- If you need help with anything related to your PR please let us know in this section -->



Diff:
diff --git a/packages/documentation/docs/icons/developing-with-icons.md b/packages/documentation/docs/icons/developing-with-icons.md
index d514a7a253f..c6e30ed15be 100644
--- a/packages/documentation/docs/icons/developing-with-icons.md
+++ b/packages/documentation/docs/icons/developing-with-icons.md
@@ -9,12 +9,17 @@ no_single_tab: true
 description: 'The integration of icons into the code varies by framework. To accommodate the diverse range of potential use cases, external icons can be integrated in addition to the existing icon set.'
 ---
 
-import Icons from '@site/src/components/IconsPreview';
 import AddIconsPlayground from '@site/docs/autogenerated/playground/add-icons.mdx';
 
 # Usage
 
-## Angular
+The package @siemens/ix-icons offers a large set of icons. It also comes with the ix-icon component that displays them in your application. Additionally custom SVG icons (that are not part of the library) can be used.
+
+As of iX version 3.0.0 not all available iX icons will be loaded automatically anymore. Only the icons actually used in the application will be part of the bundle to significantly reduce the overall bundle size. As a result, icons must now be imported explicitly.
+
+## Icon usage in supported frameworks
+
+### Angular
 
 Provide all iX icons as asset folder via `angular.json`.
 
@@ -40,7 +45,7 @@ Then you can reference iX icons by name anywhere in your application.
 <ix-icon name="star" size="32"></ix-icon>
 ```
 
-## Web components
+### Web components
 
 ```html
 <ix-icon name="star" size="16"></ix-icon>
@@ -48,7 +53,7 @@ Then you can reference iX icons by name anywhere in your application.
 <ix-icon name="star" size="32"></ix-icon>
 ```
 
-## React
+### React
 
 ```tsx
 import { iconStar } from '@siemens/ix-icons/icons';
@@ -58,7 +63,7 @@ import { iconStar } from '@siemens/ix-icons/icons';
 <IxIcon name={iconStar} size="32"></IxIcon>
 ```
 
-## Vue
+### Vue
 
 ```tsx
 import { iconStar } from '@siemens/ix-icons/icons';
@@ -74,9 +79,9 @@ Although referencing icons by name (e.g. `<ix-icon name="star"><ix-icon>`) is st
 
 <AddIconsPlayground />
 
-# Integrate external icons
+## Integrate external icons
 
-## Technical requirements
+### Technical requirements
 
 - Supported icon format is SVG
 - Each icon is a single SVG file, Sprites are not supported yet
@@ -90,6 +95,6 @@ Although referencing icons by name (e.g. `<ix-icon name="star"><ix-icon>`) is st
   <ix-icon name="https://my.example.cdn.address/assets/my-icon.svg"></ix-icon>
 ```
 
-## Internal SVG structure
+### Internal SVG structure
 
 The provided SVG icons are graphic resources whose code is mostly generated by design applications such as Figma, Sketch or Adobe Illustrator. The tools use different export routines, which can also change over time. We therefore cannot guarantee that the internal structure of an SVG will be preserved after updates – even if the icons remain visually the same.
diff --git a/packages/documentation/docs/icons/icon-library.mdx b/packages/documentation/docs/icons/icon-library.mdx
index 83ee716239e..90ff91eaf52 100644
--- a/packages/documentation/docs/icons/icon-library.mdx
+++ b/packages/documentation/docs/icons/icon-library.mdx
@@ -2,15 +2,15 @@
 sidebar_position: 0
 sidebar_label: Icon library
 title: Icon library
-hide_table_of_contents: false
+hide_table_of_contents: true
 doc-type: 'banner'
 component-tabs: ['']
 no_single_tab: true
-description: "The Siemens Industrial Experience design system comes with an extensive set of icons made for the industrial context. These icons are designed as in-app icons, used in the actual application UI and UI elements but not as application logos."
+description: 'The Siemens Industrial Experience design system comes with an extensive set of icons made for the industrial context. These icons are designed as in-app icons, used in the actual application UI and UI elements but not as application logos.'
 ---
 
 import Icons from '@site/src/components/IconsPreview';
 
 #
 
-<Icons></Icons>
+  <Icons />
diff --git a/packages/documentation/docs/icons/icon-usage.md b/packages/documentation/docs/icons/icon-usage.mdx
similarity index 66%
rename from packages/documentation/docs/icons/icon-usage.md
rename to packages/documentation/docs/icons/icon-usage.mdx
index ec0cf42ff09..31524ad5d5b 100644
--- a/packages/documentation/docs/icons/icon-usage.md
+++ b/packages/documentation/docs/icons/icon-usage.mdx
@@ -9,7 +9,10 @@ no_single_tab: true
 description: "Icons are graphical representations of terms, functions or objects. Ideally, we use them together with a descriptive text to make it easier for users to learn their meaning. "
 ---
 
-# 
+import IconTable from '@site/src/components/IconTable';
+import { IconExamplesClarity, IconExamplesStatus } from '@site/src/components/IconTable';
+
+#
 
 ## Clarity and contextual relevance
 
@@ -17,18 +20,30 @@ description: "Icons are graphical representations of terms, functions or objects
 - Use icons that are contextually appropriate and convey the intended message.
 - Avoid using icons that could be misinterpreted or confusing.
 
+Examples
+
+<IconTable data={IconExamplesClarity}></IconTable>
+
 ## Status icons
 
 - We provide a set of standard icons to convey status.
 - These icons follow a criticality hierarchy and should be used with the corresponding colors: alarm, critical, warning, success, and info.
 - To ensure consistency across different applications, do not use these icons outside of their intended use cases.
 
+Examples
+
+<IconTable data={IconExamplesStatus}></IconTable>
+
 ## Icons within components
 
 - Most of our components come with built-in icons or offer an optional icon slot.
 - Ensure consistent use of components, either with or without icons, within the same context.
 
+![Icons within components](https://www.figma.com/design/wEptRgAezDU1z80Cn3eZ0o/iX-Documentation-illustrations?node-id=5259-16957&t=ulQZQESQGAMR3uyt-4)
+
 ## Standalone icons
 
-- A standalone icon, without textual description, has to be paired with a tooltip that describes the meaning of the icon. 
+- A standalone icon, without textual description, has to be paired with a tooltip that describes the meaning of the icon.
 - Make sure a description can be read out by screen readers.
+
+![Standalone icons](https://www.figma.com/design/wEptRgAezDU1z80Cn3eZ0o/iX-Documentation-illustrations?node-id=5259-17161&t=ulQZQESQGAMR3uyt-4)
diff --git a/packages/documentation/docusaurus.config.ts b/packages/documentation/docusaurus.config.ts
index e06deef511e..0ace6ec604a 100644
--- a/packages/documentation/docusaurus.config.ts
+++ b/packages/documentation/docusaurus.config.ts
@@ -43,13 +43,13 @@ let withBrandTheme = false;
 
 try {
   const path = require.resolve(
-    '@siemens/ix-corporate-theme/dist/css/corporate-theme.css'
+    '@siemens-ix/corporate-theme/dist/css/corporate-theme.css'
   );
-  console.log('Found optionalDependency @siemens/ix-corporate-theme.');
+  console.log('Found optionalDependency @siemens-ix/corporate-theme.');
   customCss.push(path);
   withBrandTheme = true;
 } catch (e) {
-  console.warn('optionalDependency @siemens/ix-corporate-theme not found!');
+  console.warn('optionalDependency @siemens-ix/corporate-theme not found!');
 }
 
 const brokenLinks = 'throw';
diff --git a/packages/documentation/scripts/build.ts b/packages/documentation/scripts/build.ts
index a5efeefa508..ded7a983c82 100644
--- a/packages/documentation/scripts/build.ts
+++ b/packages/documentation/scripts/build.ts
@@ -373,7 +373,7 @@ async function downloadAdditionalTheme() {
 
     const renameTarget = path.join(
       __previewHtmlAdditionalTheme,
-      'ix-corporate-theme'
+      'corporate-theme'
     );
     if (fs.existsSync(renameTarget)) {
       rimrafSync(renameTarget);
@@ -383,13 +383,13 @@ async function downloadAdditionalTheme() {
     // Copy the theme to the mobile preview
     fs.copySync(
       renameTarget,
-      path.join(__previewMobileAdditionalTheme, 'ix-corporate-theme')
+      path.join(__previewMobileAdditionalTheme, 'corporate-theme')
     );
 
     // This step is necessary to make the theme available during the build process of the documentation
     fs.copySync(
       renameTarget,
-      path.join(__node_modules, '@siemens', 'ix-corporate-theme')
+      path.join(__node_modules, '@siemens', 'corporate-theme')
     );
   } catch (e: any) {
     console.timeLog('Download additional theme', e.message);
diff --git a/packages/documentation/sidebars.ts b/packages/documentation/sidebars.ts
index 69dc36d880f..8fcdb45fef1 100644
--- a/packages/documentation/sidebars.ts
+++ b/packages/documentation/sidebars.ts
@@ -1127,9 +1127,16 @@ const sidebars: SidebarsConfig = {
   ],
   icons: [
     'icons/icon-library',
-    'icons/icon-usage',
-    'icons/design-new-icons',
-    'icons/developing-with-icons',
+    {
+      type: 'category',
+      className: 'doc-sidebar-item-bold',
+      label: 'Icon guidelines',
+      items: [
+        'icons/icon-usage',
+        'icons/design-new-icons',
+        'icons/developing-with-icons',
+      ],
+    },
   ],
   styles: [
     'styles/colors',
diff --git a/packages/documentation/src/components/IconTable/index.tsx b/packages/documentation/src/components/IconTable/index.tsx
new file mode 100644
index 00000000000..3a4f4505373
--- /dev/null
+++ b/packages/documentation/src/components/IconTable/index.tsx
@@ -0,0 +1,115 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import React from 'react';
+import { IxIcon } from '@siemens/ix-react';
+import styles from './styles.module.css';
+
+const IconTable: React.FC<{
+  data: { icon: string; color?: string; name: string; description: string }[];
+}> = ({ data }) => {
+  return (
+    <table className={styles.IconTable}>
+      <tbody>
+        {data.map((item) => (
+          <tr key={item.name}>
+            <td className={styles.IconCell}>
+              <IxIcon name={item.icon} color={item.color ?? 'color-std-text'} />
+            </td>
+            <td className={styles.NameCell}>{item.name}</td>
+            <td>{item.description}</td>
+          </tr>
+        ))}
+      </tbody>
+    </table>
+  );
+};
+
+export default IconTable;
+
+export const IconExamplesClarity = [
+  {
+    icon: 'home',
+    name: 'home',
+    description: 'Use for start pages',
+  },
+  {
+    icon: 'search',
+    name: 'search',
+    description: 'Use for search inputs',
+  },
+  {
+    icon: 'pen',
+    name: 'pen',
+    description: 'Use for edit modes',
+  },
+  {
+    icon: 'cogwheel',
+    name: 'cogwheel',
+    description: 'Use for general settings',
+  },
+  {
+    icon: 'trashcan',
+    name: 'trashcan',
+    description: 'Use for delete actions',
+  },
+  {
+    icon: 'user',
+    name: 'user',
+    description: 'Use for user menus',
+  },
+  {
+    icon: 'download',
+    name: 'download',
+    description: 'Use for downloading files',
+  },
+  {
+    icon: 'upload',
+    name: 'upload',
+    description: 'Use for uploading files',
+  },
+];
+
+export const IconExamplesStatus = [
+  {
+    icon: 'alarm',
+    color: 'color-alarm',
+    name: 'alarm',
+    description: 'Use for hazardous states',
+  },
+  {
+    icon: 'error',
+    color: 'color-alarm',
+    name: 'error',
+    description: 'Use for error states',
+  },
+  {
+    icon: 'warning-rhomb',
+    color: 'color-critical',
+    name: 'warning-rhomb',
+    description: 'Use for critical states',
+  },
+  {
+    icon: 'warning',
+    color: 'color-warning',
+    name: 'warning',
+    description: 'Use for warning states',
+  },
+  {
+    icon: 'success',
+    color: 'color-success',
+    name: 'success',
+    description: 'Use for success states',
+  },
+  {
+    icon: 'info',
+    color: 'color-info',
+    name: 'info',
+    description: 'Use for info states',
+  },
+];
diff --git a/packages/documentation/src/components/IconTable/styles.module.css b/packages/documentation/src/components/IconTable/styles.module.css
new file mode 100644
index 00000000000..3713948be35
--- /dev/null
+++ b/packages/documentation/src/components/IconTable/styles.module.css
@@ -0,0 +1,36 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+.IconTable {
+  display: table;
+  width: 100%;
+  border-collapse: collapse;
+}
+
+.IconTable td {
+  height: 4.5rem;
+  padding: 0 1.5rem;
+}
+
+.IconTable tr {
+  height: 4.5rem;
+  width: 100%;
+}
+
+.IconCell {
+  width: 4.5rem;
+}
+
+.IconCell ix-icon {
+  vertical-align: middle;
+}
+
+.NameCell {
+  text-align: center;
+}
diff --git a/packages/documentation/src/components/IconsPreview/Icons.module.css b/packages/documentation/src/components/IconsPreview/Icons.module.css
index 7d75413a33d..45a59e43634 100644
--- a/packages/documentation/src/components/IconsPreview/Icons.module.css
+++ b/packages/documentation/src/components/IconsPreview/Icons.module.css
@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2023 Siemens AG
+ * SPDX-FileCopyrightText: 2025 Siemens AG
  *
  * SPDX-License-Identifier: MIT
  *
@@ -20,32 +20,37 @@
 }
 
 .Icons {
-  display: flex;
-  flex-wrap: wrap;
   height: fit-content;
   overflow: auto;
   margin-top: 1rem;
 }
 
+.Icon__Row {
+  display: flex;
+  position: relative;
+}
+
 .Icon__Tile {
   display: flex;
   flex-direction: column;
   align-items: center;
-  justify-content: center;
+  justify-content: space-between;
 
   border: var(--theme-std-bdr-1);
   border-radius: 4px;
 
-  width: 10rem;
+  width: 8rem;
   height: 5rem;
 
   margin: 0.5rem;
+  padding: calc(1rem - 1px);
 
   cursor: pointer;
 }
 
 .Icon__Tile.Selected {
   border: var(--theme-primary-bdr-1);
+  background-color: var(--theme-color-2);
 }
 
 .Icon__Tile:hover {
@@ -53,12 +58,21 @@
   transform: scale(1.1);
 }
 
+.Icon__Name,
+.Icon__Name ix-typography {
+  width: 100%;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  text-align: center;
+}
+
 .Search {
   display: flex;
   position: relative;
   align-items: center;
+  gap: 1rem;
   margin-left: 0.5rem;
-  width: 40%;
   height: auto;
 }
 
@@ -66,7 +80,115 @@
   margin-right: 0.5rem;
 }
 
-.Hide__Input {
-  border: none !important;
+.Search__NoResults {
+  width: 20rem;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  align-self: center;
+  gap: 0.5rem;
+  margin-block-start: 1.5rem;
+}
+
+.Search__NoResultsIcon {
+  width: 3.5rem;
+  height: 3.5rem;
+}
+
+.Search__NoIconsFound {
+  font-size: 1.5rem;
+}
+
+.Search__AdaptOrRequest {
+  color: var(--theme-color-soft-text);
   text-align: center;
 }
+
+.Search__NoResults a,
+.Search__NoResults a:hover {
+  text-decoration: underline;
+  color: var(--theme-color-soft-text);
+}
+
+.Icon__ContainerDetails {
+  height: 190px;
+}
+
+.Icon__Details {
+  position: absolute;
+  left: 0;
+
+  display: flex;
+  align-items: center;
+  gap: 0.625rem;
+
+  background-color: var(--theme-color-2);
+  border: var(--theme-std-bdr-1);
+  border-radius: 4px;
+
+  width: 100%;
+  height: 5rem;
+  min-height: 5rem;
+
+  margin: 0.5rem;
+  padding: calc(1rem - 1px);
+}
+
+.Icon__Details .Icon__NameContainer {
+  display: flex;
+  justify-items: center;
+  width: 50%;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.Icon__Details [class*='prism-code'] {
+  scrollbar-width: none;
+}
+
+ix-typography:focus-visible,
+.Icon__Code [class*='prism-code']:focus-visible [class*='token-line'] {
+  background-color: var(--theme-color-info-40);
+  outline: none;
+}
+
+.Icon__FlexContent {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+  flex-grow: 1;
+  gap: 0.5rem;
+}
+
+.Icon__Code[class*='codeBlockContainer'],
+.Icon__Code [class*='prism-code'] {
+  background-color: transparent !important;
+  margin-bottom: unset;
+  box-shadow: unset;
+}
+
+.Icon__CodeContainer {
+  width: 50%;
+}
+
+.Icon__Code [class*='prism-code'] {
+  cursor: pointer;
+}
+
+.Icon__Code [class*='prism-code']:focus-visible {
+  outline: none;
+}
+
+.TooltipSuccess {
+  display: flex;
+  align-items: center;
+  gap: 0.25rem;
+}
+
+@media (max-width: 1280px) {
+  .FrameworkSelectionContainer {
+    display: none;
+  }
+}
diff --git a/packages/documentation/src/components/IconsPreview/index.tsx b/packages/documentation/src/components/IconsPreview/index.tsx
index 61ace9dc84c..2bb13feace9 100644
--- a/packages/documentation/src/components/IconsPreview/index.tsx
+++ b/packages/documentation/src/components/IconsPreview/index.tsx
@@ -9,60 +9,351 @@
 
 import ICON_LIST from '@siemens/ix-icons/dist/sample.json';
 import clsx from 'clsx';
-import React, { useEffect, useMemo, useRef, useState } from 'react';
+import React, {
+  useEffect,
+  useRef,
+  useState,
+  useMemo,
+  useCallback,
+} from 'react';
 import styles from './Icons.module.css';
+import {
+  IxCheckbox,
+  IxIcon,
+  IxIconButton,
+  IxInput,
+  IxTooltip,
+  IxTypography,
+} from '@siemens/ix-react';
+import { iconClear, iconSearch, iconSuccess } from '@siemens/ix-icons/icons';
+import { FrameworkTypes } from '@site/src/hooks/use-framework';
+import FrameworkSelection from '../UI/FrameworkSelection';
+import CodeBlock from '@theme/CodeBlock';
+import { fromKebabCaseToCamelCase } from '@site/src/lib/utils/string-format';
+import { debounce } from '@site/src/lib/utils/debounce';
+import BrowserOnly from '@docusaurus/BrowserOnly';
 
-const Icons: React.FC = () => {
-  const refs = useRef<{ [k: string]: any }>({});
+function getIconCode(iconName: string, framework: FrameworkTypes) {
+  const importedName = 'icon' + fromKebabCaseToCamelCase(iconName);
+
+  switch (framework) {
+    case 'angular':
+      return `<ix-icon name="${iconName}"></ix-icon>`;
+    case 'react':
+      return `<IxIcon name={${importedName}}></IxIcon>`;
+    case 'vue':
+      return `<IxIcon :name="${importedName}"></IxIcon>`;
+    default:
+      return `<ix-icon name="${iconName}"></ix-icon>`;
+  }
+}
+
+function getColumnCount(width: number) {
+  if (width < 496) {
+    return 2;
+  } else if (width < 640) {
+    return 3;
+  } else if (width < 784) {
+    return 4;
+  } else if (width < 928) {
+    return 5;
+  } else if (width < 997) {
+    return 6;
+  } else if (width < 1216) {
+    return 4;
+  } else if (width < 1360) {
+    return 5;
+  } else if (width < 1504) {
+    return 6;
+  } else {
+    return 7;
+  }
+}
+
+const IconTiles: React.FC<{ columnCount: number; iconList: string[] }> = (
+  props
+) => {
+  const [framework, setFramework] = useState<FrameworkTypes>('angular');
+  const IconDetails = React.forwardRef<
+    HTMLDivElement,
+    { iconName: string; columnCount: number }
+  >(function (props, ref) {
+    const tooltipRef = useRef<HTMLIxTooltipElement>(null);
+    const codeBlockContainerRef = useRef<HTMLDivElement>(null);
+
+    async function copyToClipboard(text: string) {
+      await navigator.clipboard.writeText(text);
+      tooltipRef.current?.showTooltip(codeBlockContainerRef.current);
+      setTimeout(() => {
+        tooltipRef.current?.hideTooltip();
+      }, 750);
+    }
+
+    function getWidth() {
+      const tileWidth = 128;
+      const tileMargin = 16;
+      return (
+        tileWidth * props.columnCount + tileMargin * (props.columnCount - 1)
+      );
+    }
+
+    return (
+      <div
+        ref={ref}
+        style={{ width: getWidth() }}
+        className={styles.Icon__Details}
+      >
+        <IxIcon name={props.iconName} size="32" />
+        <div className={styles.Icon__FlexContent}>
+          <div className={styles.Icon__NameContainer}>
+            <IxTypography format="h3">{props.iconName}</IxTypography>
+            <a href={`#${props.iconName}`} className="hash-link"></a>
+          </div>
+
+          <div
+            ref={codeBlockContainerRef}
+            className={clsx(
+              styles.Icon__CodeContainer,
+              'code-block-no-copy',
+              'code-block-no-wrap'
+            )}
+            onClick={() => {
+              copyToClipboard(getIconCode(props.iconName, framework));
+            }}
+            onKeyDown={(e) => {
+              if (e.key === 'Enter') {
+                copyToClipboard(getIconCode(props.iconName, framework));
+              }
+            }}
+          >
+            <CodeBlock className={styles.Icon__Code} language="html">
+              {getIconCode(props.iconName, framework)}
+            </CodeBlock>
+            <IxTooltip ref={tooltipRef}>
+              <div className={styles.TooltipSuccess}>
+                <IxIcon
+                  name={iconSuccess}
+                  color="color-success"
+                  size="16"
+                ></IxIcon>
+                Copied
+              </div>
+            </IxTooltip>
+          </div>
+        </div>
+        <div className={styles.FrameworkSelectionContainer}>
+          <FrameworkSelection onFrameworkChange={(fw) => setFramework(fw)} />
+        </div>
+      </div>
+    );
+  });
+
+  const iconDetailsRef = useRef<HTMLDivElement>(null);
+  const handleIconClick = useCallback((icon: string) => {
+    setSelectedIcon((prev) => (prev === icon ? null : icon));
+    setTimeout(() => {
+      iconDetailsRef.current?.scrollIntoView({
+        behavior: 'smooth',
+        block: 'nearest',
+      });
+    }, 0);
+  }, []);
+
+  window.addEventListener('keydown', (e) => {
+    if (
+      e.key === 'Escape' &&
+      iconDetailsRef.current?.contains(document.activeElement)
+    ) {
+      setSelectedIcon(null);
+    }
+  });
 
-  const [searchIcon, setSearchIcon] = useState<string>();
+  const [selectedIcon, setSelectedIcon] = useState<string | null>();
+  const iconRows = useMemo(() => {
+    const rows = [];
+    for (let i = 0; i < props.iconList.length; i += props.columnCount) {
+      rows.push(props.iconList.slice(i, i + props.columnCount));
+    }
+    return rows;
+  }, [props.iconList, props.columnCount]);
+
+  useEffect(() => {
+    const hash = window.location.hash.replace('#', '');
+    if (hash) {
+      handleIconClick(hash);
+    }
+  }, []);
+
+  return (
+    <div className={clsx(styles.Icons)}>
+      {iconRows.map((row, rowIndex) => (
+        <div key={rowIndex}>
+          <div className={styles.Icon__Row}>
+            {row.map((icon) => (
+              <div
+                className={clsx(
+                  styles.Icon__Container,
+                  props.columnCount > 2 &&
+                    row.includes(selectedIcon) &&
+                    styles.Icon__ContainerDetails
+                )}
+                key={icon}
+              >
+                <div
+                  className={clsx(styles.Icon__Tile, {
+                    [styles.Selected]: selectedIcon === icon,
+                  })}
+                  onClick={() => handleIconClick(icon)}
+                  onKeyDown={(e) => {
+                    if (e.key === 'Enter') {
+                      handleIconClick(icon);
+                    }
+                  }}
+                >
+                  {React.createElement('ix-icon', {
+                    name: icon,
+                  })}
+                  <div className={styles.Icon__Name}>
+                    <IxTypography tabIndex={0} format="body">
+                      {icon}
+                    </IxTypography>
+                  </div>
+                </div>
+                {selectedIcon === icon && props.columnCount > 2 && (
+                  <IconDetails
+                    ref={iconDetailsRef}
+                    iconName={selectedIcon}
+                    columnCount={props.columnCount}
+                  />
+                )}
+              </div>
+            ))}
+          </div>
+        </div>
+      ))}
+    </div>
+  );
+};
+
+const Icons: React.FC = () => {
+  const [iconFilter, setIconFilter] = useState<string>();
+  const [showRegularIcons, setShowRegularIcons] = useState<boolean>(true);
+  const [showFilledIcons, setShowFilledIcons] = useState<boolean>(true);
   const [icons] = useState<string[]>(ICON_LIST.icons);
+  const [iconList, setIconList] = useState<string[]>(ICON_LIST.icons);
+  const [columnCount, setColumnCount] = useState<number>(2);
+  const filterInputRef = useRef<HTMLIxInputElement>(null);
+
+  const filteredIcons = useMemo(() => {
+    return icons.filter((icon) => {
+      const isFilled = icon.endsWith('-filled');
+      const matchesFilter =
+        !iconFilter || icon.toLowerCase().includes(iconFilter);
+
+      return (
+        ((showRegularIcons && !isFilled) || (showFilledIcons && isFilled)) &&
+        matchesFilter
+      );
+    });
+  }, [icons, iconFilter, showRegularIcons, showFilledIcons]);
+
+  useEffect(() => {
+    setIconList(filteredIcons);
+  }, [filteredIcons]);
 
-  const [selectedIcon, setSelectedIcon] = useState<string | null>(null);
+  useEffect(() => {
+    const handleResize = debounce(() => {
+      const width = window.innerWidth;
+      const count = getColumnCount(width);
+      if (count !== columnCount) {
+        setColumnCount(count);
+      }
+    }, 150);
+
+    window.addEventListener('resize', handleResize);
+    return () => window.removeEventListener('resize', handleResize);
+  }, [columnCount]);
+
+  useEffect(() => {
+    setColumnCount(getColumnCount(window.innerWidth));
+  }, []);
 
   return (
-    <div className={styles.IconsPreview}>
-      <div className={clsx(styles.Search)}>
-        <span className={clsx(styles.Search__Label)}>Search: </span>
-        <input
-          placeholder="Search Icon"
-          onInput={(e) =>
-            setSearchIcon(
-              (e.target as HTMLInputElement).value.toLocaleLowerCase()
-            )
-          }
-        />
-      </div>
-      <div className={clsx(styles.Icons)}>
-        {icons
-          .filter((icon) => !searchIcon || icon.includes(searchIcon))
-          .map((icon) => (
-            <div
-              key={icon}
-              className={clsx(styles.Icon__Tile, {
-                [styles.Selected]: selectedIcon === icon,
-              })}
-              onClick={() => {}}
+    <BrowserOnly>
+      {() => (
+        <div className={styles.IconsPreview}>
+          <div className={clsx(styles.Search)}>
+            <IxInput
+              ref={filterInputRef}
+              placeholder="Search icon"
+              onInput={(e) =>
+                setIconFilter(
+                  (e.target as HTMLInputElement).value.toLocaleLowerCase()
+                )
+              }
             >
-              <div>
-                {React.createElement('ix-icon', {
-                  name: icon,
-                })}
+              <IxIcon
+                name={iconSearch}
+                color="color-dynamic"
+                slot="start"
+                size="16"
+              ></IxIcon>
+              {iconFilter && (
+                <IxIconButton
+                  ghost
+                  oval
+                  iconColor="color-soft-text"
+                  icon={iconClear}
+                  slot="end"
+                  size="16"
+                  onClick={() => {
+                    setIconFilter('');
+                    filterInputRef.current.value = '';
+                  }}
+                ></IxIconButton>
+              )}
+            </IxInput>
+            <IxCheckbox
+              checked={showRegularIcons}
+              label="Show regular icons"
+              onCheckedChange={(e) => {
+                setShowRegularIcons(e.detail);
+              }}
+            ></IxCheckbox>
+            <IxCheckbox
+              checked={showFilledIcons}
+              label="Show filled icons"
+              onCheckedChange={(e) => {
+                setShowFilledIcons(e.detail);
+              }}
+            ></IxCheckbox>
+          </div>
+
+          <IconTiles columnCount={columnCount} iconList={iconList} />
+
+          {iconList.length === 0 && (
+            <div className={styles.Search__NoResults}>
+              <IxIcon
+                className={styles.Search__NoResultsIcon}
+                name="search"
+                color="color-soft-text"
+              ></IxIcon>
+              <div className={styles.Search__NoIconsFound}>No icons found</div>
+              <div className={styles.Search__AdaptOrRequest}>
+                Adapt the filter<br></br> or open an icon request in{' '}
+                <a
+                  target="_blank"
+                  href="https://github.com/siemens/ix-icons/issues"
+                >
+                  GitHub
+                </a>
+                .
               </div>
-              <input
-                ref={(r) => {
-                  refs.current[icon] = r;
-                }}
-                className={clsx(styles.Hide__Input)}
-                type="text"
-                readOnly
-                value={icon}
-                onChange={() => {}}
-              ></input>
             </div>
-          ))}
-      </div>
-    </div>
+          )}
+        </div>
+      )}
+    </BrowserOnly>
   );
 };
 
diff --git a/packages/documentation/src/components/UI/CopyButton/CopyButton.module.css b/packages/documentation/src/components/UI/CopyButton/CopyButton.module.css
index 2cd935dadb2..f2925368db4 100644
--- a/packages/documentation/src/components/UI/CopyButton/CopyButton.module.css
+++ b/packages/documentation/src/components/UI/CopyButton/CopyButton.module.css
@@ -9,3 +9,9 @@
 .CopyButton {
   padding-right: 0.25rem;
 }
+
+.TooltipSuccess {
+  display: flex;
+  align-items: center;
+  gap: 0.25rem;
+}
diff --git a/packages/documentation/src/components/UI/CopyButton/index.tsx b/packages/documentation/src/components/UI/CopyButton/index.tsx
index b58458203cf..957926d4d15 100644
--- a/packages/documentation/src/components/UI/CopyButton/index.tsx
+++ b/packages/documentation/src/components/UI/CopyButton/index.tsx
@@ -6,8 +6,8 @@
  * This source code is licensed under the MIT license found in the
  * LICENSE file in the root directory of this source tree.
  */
-import { iconCopy } from '@siemens/ix-icons/icons';
-import { IxTooltip } from '@siemens/ix-react';
+import { iconCopy, iconSuccess } from '@siemens/ix-icons/icons';
+import { IxIcon, IxTooltip } from '@siemens/ix-react';
 import React, { useRef } from 'react';
 import Button from '../Button';
 import clsx from 'clsx';
@@ -64,7 +64,12 @@ const CopyButton = ({
           {preview}
         </IxTooltip>
       )}
-      <IxTooltip ref={tooltipRef}>✅ Copied to clipboard</IxTooltip>
+      <IxTooltip ref={tooltipRef}>
+        <div className={styles.TooltipSuccess}>
+          <IxIcon name={iconSuccess} color="color-success" size="16"></IxIcon>
+          Copied
+        </div>
+      </IxTooltip>
     </div>
   );
 };
diff --git a/packages/documentation/src/scss/overwrite/_code-block.scss b/packages/documentation/src/scss/overwrite/_code-block.scss
index 7805e933182..95c37b6f520 100644
--- a/packages/documentation/src/scss/overwrite/_code-block.scss
+++ b/packages/documentation/src/scss/overwrite/_code-block.scss
@@ -14,4 +14,10 @@
       display: none;
     }
   }
+
+  .code-block-no-wrap {
+    .theme-code-block [class*='buttonGroup'] button[title='Toggle word wrap'] {
+      display: none;
+    }
+  }
 }
diff --git a/packages/documentation/src/theme/DocItem/Layout/index.tsx b/packages/documentation/src/theme/DocItem/Layout/index.tsx
index c356711e896..686d3f30f8e 100644
--- a/packages/documentation/src/theme/DocItem/Layout/index.tsx
+++ b/packages/documentation/src/theme/DocItem/Layout/index.tsx
@@ -83,7 +83,12 @@ export function BannerDocItemLayout({ children }: Props): JSX.Element {
         tabs={[]}
         frontMatter={{ ...metadata.frontMatter, no_single_tab: false }}
       />
-      <div className={styles.Row}>
+      <div
+        className={clsx(
+          styles.Row,
+          metadata.frontMatter.hide_table_of_contents && styles.RowHideToc
+        )}
+      >
         <div className={styles.docItemContainer}>
           <ContentVisibility metadata={metadata} />
           <DocVersionBanner />
diff --git a/packages/documentation/src/theme/DocItem/Layout/styles.module.css b/packages/documentation/src/theme/DocItem/Layout/styles.module.css
index e40a80a8d6a..85a3a430570 100644
--- a/packages/documentation/src/theme/DocItem/Layout/styles.module.css
+++ b/packages/documentation/src/theme/DocItem/Layout/styles.module.css
@@ -16,6 +16,11 @@
   max-width: 1200px;
 }
 
+.RowHideToc {
+  grid-template-columns: 100%;
+  max-width: none;
+}
+
 .toc {
   max-width: 14rem;
 }
diff --git a/packages/html-test-app/vite.config.ts b/packages/html-test-app/vite.config.ts
index f92d294ff8c..d8974668cae 100644
--- a/packages/html-test-app/vite.config.ts
+++ b/packages/html-test-app/vite.config.ts
@@ -28,7 +28,7 @@ entryPoints.forEach((file) => {
 });
 
 const additionalTheme = {
-  css: 'ix-corporate-theme/dist/css/brand-theme.css',
+  css: 'corporate-theme/dist/css/brand-theme.css',
 };
 
 // https://vitejs.dev/config/
diff --git a/packages/ionic-test-app/index.html b/packages/ionic-test-app/index.html
index fff602fcf1e..b8447f5431d 100644
--- a/packages/ionic-test-app/index.html
+++ b/packages/ionic-test-app/index.html
@@ -24,7 +24,7 @@
     <!-- Optional theme only used in documentation -->
     <link
       rel="stylesheet"
-      href="./additional-theme/ix-corporate-theme/dist/css/brand-theme.css"
+      href="./additional-theme/corporate-theme/dist/css/brand-theme.css"
     />
   </head>
   <body class="theme-brand-dark">
diff --git a/packages/ionic-test-app/vite.config.ts b/packages/ionic-test-app/vite.config.ts
index 52a3ae0a0f5..3b60334af6f 100644
--- a/packages/ionic-test-app/vite.config.ts
+++ b/packages/ionic-test-app/vite.config.ts
@@ -8,7 +8,7 @@ import fs from 'fs-extra';
 
 function copyAdditionalThemeIfPresent() {
   try {
-    const additionalTheme = import.meta.resolve('@siemens/ix-corporate-theme');
+    const additionalTheme = import.meta.resolve('@siemens-ix/corporate-theme');
     const basePath = path.join(
       additionalTheme.replace('file://', ''),
       '..',
@@ -18,7 +18,7 @@ function copyAdditionalThemeIfPresent() {
       __dirname,
       'public',
       'additional-theme',
-      'ix-corporate-theme'
+      'corporate-theme'
     );
     const distPath = path.join(basePath, 'dist');
 


Reviews:
user: login: danielleroux
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T07:23:11Z
user: login: danielleroux
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T07:59:49Z
user: login: danielleroux
body: Would be nice if the icons are linkable via an anchor.
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T08:22:00Z
user: login: danielleroux
body: After changing the corporate theme name there are still the wrong names used inside ionic test app:
![image](https://github.com/user-attachments/assets/d3568dc5-c0a4-4d5a-bb48-165a596c0efe)

state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T05:48:08Z
user: login: danielleroux
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-23T08:41:13Z

Review comments:
diff_hunk: @@ -43,9 +43,9 @@ let withBrandTheme = false;
 
 try {
   const path = require.resolve(
-    '@siemens/ix-corporate-theme/dist/css/corporate-theme.css'
+    '@siemens-ix/corporate-theme/dist/css/corporate-theme.css'
   );
-  console.log('Found optionalDependency @siemens/ix-corporate-theme.');
+  console.log('Found optionalDependency @siemens-ix/corporate-theme.');
path: packages/documentation/docusaurus.config.ts
user: login: danielleroux
body: Fixing the path here needs additional fixes here:

https://github.com/siemens/ix/blob/f837fcc114276614adbb8c7cb51e4daee5b40d6f/packages/documentation/scripts/build.ts#L386

https://github.com/siemens/ix/blob/f837fcc114276614adbb8c7cb51e4daee5b40d6f/packages/html-test-app/vite.config.ts#L31

https://github.com/siemens/ix/blob/f837fcc114276614adbb8c7cb51e4daee5b40d6f/packages/ionic-test-app/vite.config.ts#L11

Otherwise previews will no showing the theme anymore.
created_at: 2025-04-22T07:22:30Z
author_association: COLLABORATOR
diff_hunk: @@ -9,59 +9,329 @@
 
 import ICON_LIST from '@siemens/ix-icons/dist/sample.json';
 import clsx from 'clsx';
-import React, { useEffect, useMemo, useRef, useState } from 'react';
+import React, {
+  useEffect,
+  useRef,
+  useState,
+  useMemo,
+  useCallback,
+} from 'react';
 import styles from './Icons.module.css';
+import {
+  IxCheckbox,
+  IxIcon,
+  IxIconButton,
+  IxInput,
+  IxTooltip,
+  IxTypography,
+} from '@siemens/ix-react';
+import { iconClear, iconSearch, iconSuccess } from '@siemens/ix-icons/icons';
+import { FrameworkTypes } from '@site/src/hooks/use-framework';
+import FrameworkSelection from '../UI/FrameworkSelection';
+import CodeBlock from '@theme/CodeBlock';
+import { fromKebabCaseToCamelCase } from '@site/src/lib/utils/string-format';
+import { debounce } from '@site/src/lib/utils/debounce';
 
-const Icons: React.FC = () => {
-  const refs = useRef<{ [k: string]: any }>({});
+function getIconCode(iconName: string, framework: FrameworkTypes) {
+  const importedName = 'icon' + fromKebabCaseToCamelCase(iconName);
+
+  switch (framework) {
+    case 'angular':
+      return `<ix-icon name="${iconName}"></ix-icon>`;
+    case 'react':
+      return `<IxIcon name={${importedName}}></IxIcon>`;
+    case 'vue':
+      return `<IxIcon :name="${importedName}"></IxIcon>`;
+    default:
+      return `<ix-icon name="${iconName}"></ix-icon>`;
+  }
+}
+
+function getColumnCount(width: number) {
+  if (width < 496) {
+    return 2;
+  } else if (width < 640) {
+    return 3;
+  } else if (width < 784) {
+    return 4;
+  } else if (width < 928) {
+    return 5;
+  } else if (width < 997) {
+    return 6;
+  } else if (width < 1216) {
+    return 4;
+  } else if (width < 1360) {
+    return 5;
+  } else if (width < 1504) {
+    return 6;
+  } else {
+    return 7;
+  }
+}
+
+const IconTiles: React.FC<{ columnCount: number; iconList: string[] }> = (
+  props
+) => {
+  const [framework, setFramework] = useState<FrameworkTypes>('angular');
+  const IconDetails = React.forwardRef<
+    HTMLDivElement,
+    { iconName: string; columnCount: number }
+  >(function (props, ref) {
+    const tooltipRef = useRef<HTMLIxTooltipElement>(null);
+    const codeBlockContainerRef = useRef<HTMLDivElement>(null);
+
+    async function copyToClipboard(text: string) {
+      await navigator.clipboard.writeText(text);
+      tooltipRef.current?.showTooltip(codeBlockContainerRef.current);
+      setTimeout(() => {
+        tooltipRef.current?.hideTooltip();
+      }, 750);
+    }
+
+    function getWidth() {
+      const tileWidth = 128;
+      const tileMargin = 16;
+      return (
+        tileWidth * props.columnCount + tileMargin * (props.columnCount - 1)
+      );
+    }
+
+    return (
+      <div
+        ref={ref}
+        style={{ width: getWidth() }}
+        className={styles.Icon__Details}
+      >
+        <IxIcon name={props.iconName} size="32" />
+        <div className={styles.Icon__FlexContent}>
+          <IxTypography format="h3">{props.iconName}</IxTypography>
+          <div
+            ref={codeBlockContainerRef}
+            className={clsx(
+              styles.Icon__CodeContainer,
+              'code-block-no-copy',
+              'code-block-no-wrap'
+            )}
+            onClick={() => {
+              copyToClipboard(getIconCode(props.iconName, framework));
+            }}
+            onKeyDown={(e) => {
+              if (e.key === 'Enter') {
+                copyToClipboard(getIconCode(props.iconName, framework));
+              }
+            }}
+          >
+            <CodeBlock className={styles.Icon__Code} language="html">
+              {getIconCode(props.iconName, framework)}
+            </CodeBlock>
+            <IxTooltip ref={tooltipRef}>
+              <div className={styles.TooltipSuccess}>
+                <IxIcon
+                  name={iconSuccess}
+                  color="color-success"
+                  size="16"
+                ></IxIcon>
+                Copied
+              </div>
+            </IxTooltip>
+          </div>
+        </div>
+        <div className={styles.FrameworkSelectionContainer}>
+          <FrameworkSelection onFrameworkChange={(fw) => setFramework(fw)} />
+        </div>
+      </div>
+    );
+  });
+
+  const iconDetailsRef = useRef<HTMLDivElement>(null);
+  const handleIconClick = useCallback((icon: string) => {
+    setSelectedIcon((prev) => (prev === icon ? null : icon));
+    setTimeout(() => {
+      iconDetailsRef.current?.scrollIntoView({
+        behavior: 'smooth',
+        block: 'nearest',
+      });
+    }, 0);
+  }, []);
+
+  window.addEventListener('keydown', (e) => {
+    if (
+      e.key === 'Escape' &&
+      iconDetailsRef.current?.contains(document.activeElement)
+    ) {
+      setSelectedIcon(null);
+    }
+  });
+
+  const [selectedIcon, setSelectedIcon] = useState<string | null>();
+  const iconRows = useMemo(() => {
+    const rows = [];
+    for (let i = 0; i < props.iconList.length; i += props.columnCount) {
+      rows.push(props.iconList.slice(i, i + props.columnCount));
+    }
+    return rows;
+  }, [props.iconList, props.columnCount]);
+
+  return (
+    <div className={clsx(styles.Icons)}>
+      {iconRows.map((row, rowIndex) => (
+        <div key={rowIndex}>
+          <div className={styles.Icon__Row}>
+            {row.map((icon) => (
+              <div
+                className={clsx(
+                  styles.Icon__Container,
+                  props.columnCount > 2 &&
+                    row.includes(selectedIcon) &&
+                    styles.Icon__ContainerDetails
+                )}
+              >
+                <div
+                  key={icon}
path: packages/documentation/src/components/IconsPreview/index.tsx
user: login: danielleroux
body: `key` attribute should be on the parent div
created_at: 2025-04-22T07:56:01Z
author_association: COLLABORATOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-04-17T14:54:00Z
body: ###  ⚠️  No Changeset found

Latest commit: 6731f60a116f5cb19d3ccf4e32ccea9309c4b1d3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/siemens/ix/new/docs/2491-new-icons-docs?filename=.changeset/cold-houses-poke.md&value=---%0A%22documentation%22%3A%20patch%0A%22html-test-app%22%3A%20patch%0A%22ionic-test-app%22%3A%20patch%0A---%0A%0Adocs(icons)%3A%20update%20section%0A)


author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-23T08:00:03Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=siemens_ix&pullRequest=1844) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [6.1% Duplication on New Code](https://sonarcloud.io/component_measures?id=siemens_ix&pullRequest=1844&metric=new_duplicated_lines_density&view=list) (required ≤ 3%)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=siemens_ix&pullRequest=1844)


author_association: NONE