diff --git a/src/components/transitions/CardTransitionGroup.vue b/src/components/transitions/CardTransitionGroup.vue
new file mode 100644
index 0000000000000000000000000000000000000000..677b5fac67f0363950f3774b8162db4e42f261d1
--- /dev/null
+++ b/src/components/transitions/CardTransitionGroup.vue
@@ -0,0 +1,31 @@
+<script setup lang="ts">
+defineProps({
+  tag: { type: String, required: false, default: "div" },
+});
+</script>
+
+<template>
+  <transition-group name="card" :tag="tag">
+    <slot></slot>
+  </transition-group>
+</template>
+
+<style>
+.card-move, /* apply transition to moving elements */
+.card-enter-active,
+.card-leave-active {
+  transition: all 0.5s ease;
+}
+
+.card-enter-from,
+.card-leave-to {
+  opacity: 0;
+  transform: scale(0);
+}
+
+/* ensure leaving items are taken out of layout flow so that moving
+   animations can be calculated correctly. */
+.card-leave-active {
+  position: absolute;
+}
+</style>
diff --git a/src/components/transitions/ListTransitionGroup.vue b/src/components/transitions/ListTransitionGroup.vue
new file mode 100644
index 0000000000000000000000000000000000000000..59f342d62396e5ad6f15e1adeb0cca58299d1f56
--- /dev/null
+++ b/src/components/transitions/ListTransitionGroup.vue
@@ -0,0 +1,31 @@
+<script setup lang="ts">
+defineProps({
+  tag: { type: String, required: false, default: "div" },
+});
+</script>
+
+<template>
+  <transition-group name="list" :tag="tag">
+    <slot></slot>
+  </transition-group>
+</template>
+
+<style>
+.list-move, /* apply transition to moving elements */
+.list-enter-active,
+.list-leave-active {
+  transition: all 0.3s ease;
+}
+
+.list-enter-from,
+.list-leave-to {
+  transform: rotateX(90deg);
+  transform-origin: center top;
+}
+
+/* ensure leaving items are taken out of layout flow so that moving
+   animations can be calculated correctly. */
+.list-leave-active {
+  position: absolute;
+}
+</style>
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index 42766a2e6c7c74142d5cfd5776a41aee760233e6..4c09afb658bea79934aa7484be0fcb0f99ccedb4 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -139,7 +139,7 @@ onMounted(() => {
         <button
           v-for="(s3key, index) in keyState.keys"
           :key="s3key.access_key"
-          class="btn w-100 fs-5 mb-3"
+          class="btn w-100 fs-5 mb-3 text-truncate"
           type="button"
           @click="keyState.activeKey = index"
           :class="{
diff --git a/src/views/workflows/ListWorkflowsView.vue b/src/views/workflows/ListWorkflowsView.vue
index f840a23c013103f2f305c954ae8f72a09e85febd..8a35758c9b0b8b6cdba79b3755d27fd662597685 100644
--- a/src/views/workflows/ListWorkflowsView.vue
+++ b/src/views/workflows/ListWorkflowsView.vue
@@ -4,6 +4,7 @@ import type { ComputedRef } from "vue";
 import { useWorkflowStore } from "@/stores/workflows";
 import type { WorkflowOut } from "@/client/workflow";
 import WorkflowCard from "@/components/workflows/WorkflowCard.vue";
+import CardTransitionGroup from "@/components/transitions/CardTransitionGroup.vue";
 import dayjs from "dayjs";
 import BootstrapIcon from "@/components/BootstrapIcon.vue";
 
@@ -155,7 +156,7 @@ onMounted(() => {
         workflowsState.filterString
       }}'
     </div>
-    <div
+    <CardTransitionGroup
       v-else
       class="d-flex flex-wrap align-items-center justify-content-between"
     >
@@ -165,7 +166,7 @@ onMounted(() => {
         :workflow="workflow"
         :loading="false"
       />
-    </div>
+    </CardTransitionGroup>
   </div>
   <div
     v-else