洗い出し:https://halved-coelurus-ca1.notion.site/26251c6c78d5809b983cea7181dadcde?source=copy_link

https://www.notion.so/2-25c51c6c78d580848361cc4f4d0da7e7?source=copy_link

aaaaa

<!DOCTYPE html>
<html xmlns:th="<http://www.thymeleaf.org>">

<head>
    <meta charset="UTF-8">
    <title>動物検索</title>
    <link rel="stylesheet" href="main-style.css" type="text/css">
</head>

<body>
    <div class="container add-control">
        <input type="radio" id="tab1" class="radio" name="tab" checked="checked"><label class="tab-title title1"
            for="tab1">未回答</label>
        <input type="radio" id="tab2" class="radio" name="tab"><label class="tab-title title2" for="tab2">作成物</label>
        <input type="radio" id="tab3" class="radio" name="tab"><label class="tab-title title3" for="tab3">tab3</label>
        <div class="tab-body">
            <div class="body1">
                <!-- <h1>未回答のアンケート</h1> -->
                 <ul class="card-display">
               <li ><a href=""><div class=image-wrapper ><img src="image.png" alt="ユニバ" > <div class="text-overlay">未回答のアンケート</div></div></a></li>
               <li ><a href=""><div class=image-wrapper ><img src="image.png" alt="ユニバ" > <div class="text-overlay">未回答のアンケート</div></div></a></li>
               <li ><a href=""><div class=image-wrapper ><img src="image.png" alt="ユニバ" > <div class="text-overlay">未回答のアンケート</div></div></a></li>
               <li ><a href=""><div class=image-wrapper ><img src="image.png" alt="ユニバ" > <div class="text-overlay">未回答のアンケート</div></div></a></li>
               <li ><a href=""><div class=image-wrapper ><img src="image.png" alt="ユニバ" > <div class="text-overlay">未回答のアンケート</div></div></a></li>
               <li ><a href=""><div class=image-wrapper ><img src="image.png" alt="ユニバ" > <div class="text-overlay">未回答のアンケート</div></div></a></li>
                <!-- <li class="card-display2"><a href="" >未回答のアンケート</a></li> -->

                <!-- <a href="" class="card-display">未回答のアンケート</a> -->
                 </ul>
            </div>
            <div class="body2">
                <h1>虫を検索</h1>
                <form method="get" action="/search">
                    <input type="text" name="keyword" placeholder="動物名" th:value="${keyword}">
                    <button type="submit">検索</button>
                </form>

                <div th:if="${animals}">
                    <h2>検索結果:</h2>
                    <ul>
                        <li th:each="animal : ${animals}" th:text="${animal.name + '(' + animal.type + ')'}"></li>
                    </ul>
                </div>
            </div>
            <div class="body3">body3 body3 body3</div>
        </div>
    </div>

</body>

</html>
/* .title1,
.body1 {
    border: 2px solid rgb(135, 136, 138);
}

.title2,
.body2 {
    border: 2px solid rgb(135, 136, 138);
}

.title3,
.body3 {
    border: 2px solid rgb(135, 136, 138);
} */

.tab-title {
    /* border-bottom: 2rem; */
    padding: .3em .5em;
    /* border-radius: .3em .3em 0 0; */
    /* text-align: center; */
    font-size: 2rem;
    /* display: table;  */
    display: flex;
    /* width: 100%; */
    /* background-color: #f3f3f3; */
}

.tab-body>div {
    width: 100%;
    height: 200px;
    border-radius: 0 .3em .3em .3em;
    padding: 1em;
}

/* radio non-display */
.container .radio {
    display: none;
    gap: 10px;
}

/* tabs position */
.container {
    display: flex;
    flex-wrap: wrap;
    position: relative;
}

.container::after {
    content: "";
    width: 90%;
}

.container .tab-title {
    position: relative;
    border-bottom: 2px solid transparent;
    top: 2px;
    left: 2px;
}

.container .tab-title:hover {
    cursor: pointer;
}

.container .tab-body {
    order: 1;
    width: 100%;
}

/* tab's body init */
.add-control .tab-body>div {
    display: none;
}

/* selected tab's color change */
.add-control .radio:checked+.tab-title {
    color: #4603fd;
    border-bottom-color: #4603fd;
}

.add-control #tab1:checked~.title1 {
    /* background: rgb(135, 136, 138); */
}

.add-control #tab2:checked~.title2 {
    /* background: rgb(135, 136, 138); */
}

.add-control #tab3:checked~.title3 {
    /* background: rgb(135, 136, 138); */
}

/* tabs control */
.add-control #tab1:checked~.tab-body>.body1 {
    display: flex;
}

.add-control #tab2:checked~.tab-body>.body2 {
    display: block;
}

.add-control #tab3:checked~.tab-body>.body3 {
    display: block;
}

.card-display {
    display: flex;
    gap: 20px;
    flex-wrap: wrap;
    list-style: none;
    /* padding-left: 0; */
    /* width:fit-content;
    height:fit-content;
    gap: 10px;
    padding: 3rem 1rem 3rem 1rem;
    border-radius: 5px;
    background-color: aliceblue; */

}

.card-display2 {
    display: flex;
    min-width: fit-content;
    min-height: fit-content;
    gap: 10px;
    padding: 3rem 1rem 3rem 1rem;
    border-radius: 5px;
    background-color: aliceblue;
    margin: 1rem;
}

.text-overlay {
    position: absolute;
    bottom: 0px;
    /* 下に配置。topにすれば上に表示 */
    left: 0px;
    right: 0px;
    color: rgb(27, 27, 27);
    background-color: rgba(252, 252, 252, 0.6);
    /* 半透明背景で文字を読みやすく */
    padding: 10%  ;
    font-size: 1.2rem;
    border-radius: 7px 7px 0 0;
}

.image-wrapper {
    position: relative;
    display: flex;
    min-width: 100%;
}

.image-wrapper img {
    min-width: 100%;
    display: block;
}
import React from "react";
import "./button.css";

function BaseButton  ({ label, onClick, className }) {
  return (
    <button className={className}
      onClick={onClick}
    >
      {label}
    </button>
  );
};
export default BaseButton;

//functionとconstの書き方の違いはこれだけ
// const BaseButton = ({ label, onClick, className }) => {
//   return (
//     <button className={className}
//       onClick={onClick}
//     >
//       {label}
//     </button>
//   );
// };
// export default BaseButton;

  .base-button {
  margin:1rem;
  background-color:aqua;
  border-radius: 10px;
  border:none;
  }

  .base-button::after{
    content: 'してね?';
    color: red;
  }

  .base-button:hover{
    background-color: antiquewhite;
  }

  .reverse-button {
  margin:1rem;
  background-color:rgb(0, 255, 115);
  border-radius: 10px;
  border:none;
  }

  .reverse-button::after{
    content: 'してね?';
    color: rgb(255, 0, 179);
  }

  .reverse-button:hover{
    background-color: rgb(230, 133, 8);
  }
 <div>
      <h1>Reactボタンコンポーネントの例</h1>
      <BaseButton label="クリックしてね" onClick={handleClick} className="reverse-button"/>
    </div>
import { useState } from 'react';
import { QRCodeSVG } from 'qrcode.react';

const QRCodeGenerator = () => {
    const [url, setUrl] = useState('<https://www.dentsusoken.com/>');

    return (
        <div>
            <h1>QRコード生成</h1>
            <input 
                type="text" 
                value={url} 
                onChange={(e) => setUrl(e.target.value)} 
                placeholder="URLを入力してください" 
            />
            <QRCodeSVG value={url} size={130} />
        </div>
    );
};

export default QRCodeGenerator;
import "./styles.css";

import React, { useState } from 'react'; // 吹き出し用のCSSをここに記述

const Copy = () => {
  const [showFukidashi, setShowFukidashi] = useState(false);
  const copyText = 'このテキストをクリップボードにコピー';

  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(copyText);
      setShowFukidashi(true);

      // 2秒後に吹き出しを非表示
      setTimeout(() => {
        setShowFukidashi(false);
      }, 2000);
    } catch (err) {
      console.error('コピーに失敗しました:', err);
    }
  };

  return (
    <div className="copy-box">
      <div className="copy-txt">{copyText}</div>
      <button
        className={`copy-btn ${showFukidashi ? '-add-fukidashi' : ''}`}
        onClick={handleCopy}
      >
        コピー
      </button>
    </div>
  );
};

export default Copy;

.progress-container {
  width: 100%;
  height: 30px;
  position: relative;
  background-color: #eee;
  border-radius: 15px;
  overflow: hidden;
}

.progress-track {
  width: 100%;
  height: 100%;
  position: relative;
  background-color: #ddd;
  border-radius: 15px;
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  background-color: #4caf50;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  transition: width 0.3s ease-in-out;
}

/* .progress-icon {
  position: absolute;
  right: -15px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 20px;
} */

.progress-img {
  position: absolute;
  right: -15px; /* 進捗バーの先頭から少し外に出す */
  top: 50%;
  transform: translateY(-50%);
  height: 24px; /* 画像サイズ調整 */
  width: 24px;
  object-fit: contain;
}

import React from "react";
import "./styles.css"; // スタイルは別ファイルで管理

const ProgressBar = ({ progress }) => {
  return (
    <div className="progress-container">
      <div className="progress-track">
        <div
          className="progress-fill"
          style={{ width: `${progress}%` }}
        >
          {/* <div className="progress-icon">🌟</div> */}
           <img src={"./iz.jpeg"} alt="progress icon" className="progress-img" />
        </div>
      </div>
    </div>
  );
};

export default ProgressBar;


.copy-box {
  position: relative;
  display: inline-block;
}

.copy-btn {
  padding: 8px 16px;
  cursor: pointer;
}

/* 吹き出し用のスタイル */
.copy-btn.-add-fukidashi::after {
  content: 'コピーしました!';
  position: absolute;
  top: -30px;
  left: 50%;
  transform: translateX(-50%);
  background: #333;
  color: #fff;
  padding: 6px 10px;
  border-radius: 4px;
  font-size: 12px;
  white-space: nowrap;
}

qr-wrapper {
  position: relative;
  width: 200px;
  height: 200px;
}

.qrcode {
  width: 100%;
  height: 100%;
  display: block;
}

.qrcode-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px; /* アイコンサイズ調整 */
  height: 40px;
  border-radius: 8px;
  background-color: white; /* 目立たせたい場合に追加可能 */
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.2); /* 立体感の追加例 */
}

  <div className="qr-wrapper">
            <QRCodeSVG value={url}  className="qrcode"/>
             <img src={"./iz.jpeg"} alt="progress icon" className="qrcode-icon" />
             </div>

.progress-img {
  position: absolute;
  right: -15px; /* 進捗バーの先頭から少し外に出す */
  top: 50%;
  transform: translateY(-50%);
  height: 24px; /* 画像サイズ調整 */
  width: 24px;
  object-fit: contain;
  animation: shake 0.5s infinite;
}

@keyframes shake {
  0% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  50% { transform: translateX(5px); }
  75% { transform: translateX(-5px); }
  100% { transform: translateX(0); }
}

reactメモ

SWRの読み込み表示処理の際、useStateを使うことが多いがなぜ

SWRの読み込み表示処理でuseStateを使うことが多い理由は以下のようなものがあります:

1. UIの状態管理

SWRはデータフェッチングに特化しているため、ローディング状態やエラー状態などのUIの表示を制御するためにuseStateが必要になることがあります。SWRは{data, error, isLoading}などの状態を返しますが、追加のUI状態管理にはuseStateが便利です。