diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,8 @@
+# Changelog for `hgg-dataframe`
+
+## 0.1.0.0 — 2026-08-05
+
+First public release on Hackage.
+
+- `PlotData` instance for the Hackage `dataframe` package's `DataFrame`,
+  so `df |>> spec` works directly on a `DataFrame` read by `readCsv`.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+BSD 3-Clause License
+
+Copyright (c) 2026, Toshiaki Honda
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from this
+   software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/hgg-dataframe.cabal b/hgg-dataframe.cabal
new file mode 100644
--- /dev/null
+++ b/hgg-dataframe.cabal
@@ -0,0 +1,65 @@
+cabal-version:      3.0
+name:               hgg-dataframe
+version:            0.1.0.0
+extra-doc-files:    CHANGELOG.md
+synopsis:           Hackage dataframe binding for hgg (PlotData instance for DataFrame)
+description:
+  Bridges the Hackage @dataframe@ package to hgg, so a @DataFrame@ can be
+  plotted directly by column name.
+  .
+  * @instance PlotData DataFrame@ — makes @df '|>>' spec@ accept a
+    @DataFrame@ (e.g. one returned by @readCsv@) with no conversion step.
+  * @dfResolver@ / @plotDF@ — the underlying resolver and a one-call save
+    helper.
+  .
+  Nullable columns are supported: a @Maybe@ column can be referenced by
+  name and missing entries are dropped, mirroring ggplot2's @na.rm@.
+  .
+  @hgg-core@ and @hgg-frame@ stay independent of any dataframe library;
+  this package is the opt-in binding for users of Hackage @dataframe@.
+license:            BSD-3-Clause
+license-file:       LICENSE
+homepage:           https://github.com/frenzieddoll/hgg
+author:             Toshiaki Honda
+maintainer:         frenzieddoll@gmail.com
+copyright:          2026 Aelysce Project (Toshiaki Honda)
+category:           Graphics
+build-type:         Simple
+
+common warnings
+  ghc-options:        -Wall -Wcompat -Widentities -Wincomplete-record-updates
+                      -Wincomplete-uni-patterns -Wpartial-fields
+                      -Wredundant-constraints
+
+library
+  import:           warnings
+  exposed-modules:  Graphics.Hgg.DataFrame
+  hs-source-dirs:   src
+  build-depends:    base                >= 4.17 && < 5
+                  , text                >= 2.0  && < 2.2
+                  , vector              >= 0.13 && < 0.14
+                  , dataframe-core       ^>= 1.1
+                  , dataframe-operations >= 1.1.1 && < 1.2
+                  , hgg-core   ^>= 0.1
+                  , hgg-frame  ^>= 0.1
+                  , hgg-svg    ^>= 0.1
+  default-language: Haskell2010
+
+test-suite hgg-dataframe-tests
+  import:           warnings
+  type:             exitcode-stdio-1.0
+  main-is:          Spec.hs
+  hs-source-dirs:   test
+  build-depends:    base
+                  , text
+                  , vector
+                  , containers
+                  , directory           >= 1.3  && < 1.4
+                  , filepath            >= 1.4  && < 1.6
+                  , dataframe-core       ^>= 1.1
+                  , hgg-core
+                  , hgg-frame
+                  , hgg-svg
+                  , hgg-dataframe
+                  , hspec               >= 2.10 && < 2.12
+  default-language: Haskell2010
diff --git a/src/Graphics/Hgg/DataFrame.hs b/src/Graphics/Hgg/DataFrame.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/Hgg/DataFrame.hs
@@ -0,0 +1,113 @@
+-- |
+-- Module      : Graphics.Hgg.DataFrame
+-- Description : DataFrame ↔ hgg Resolver bridge (Phase 26 §A 拡張)
+-- Copyright   : (c) 2026 Aelysce Project (Toshiaki Honda)
+-- License     : BSD-3-Clause
+--
+-- @
+-- import qualified DataFrame.IO.CSV            as DF
+-- import           Graphics.Hgg.Easy
+-- import           Graphics.Hgg.Backend.SVG    (saveSVGWith)
+-- import           Graphics.Hgg.DataFrame      (dfResolver, plotDF)
+--
+-- main = do
+--   df <- DF.readCsv "data.csv"
+--   plotDF "out.svg" df $
+--     purePlot
+--       <> layer (scatter "weight" "mpg" <> colorBy "origin")
+--       <> title  "燃費 vs 重量"
+-- @
+{-# LANGUAGE FlexibleContexts    #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
+-- | 'PlotData' instance for Hackage @dataframe@ は orphan (型もクラスも当 package
+-- が所有しないが、 spec-2 §3.1/§6 の設計で **橋 package が所有**する正当な配置)。
+{-# OPTIONS_GHC -Wno-orphans #-}
+module Graphics.Hgg.DataFrame
+  ( dfResolver
+  , plotDF
+    -- * Phase 14: PlotData instance (df |>> spec を使えるように)
+    -- $plotdata
+  ) where
+
+import           Graphics.Hgg.Backend.SVG (saveSVGWith)
+import           Graphics.Hgg.Frame       (PlotData (..))
+import           Graphics.Hgg.Spec        (ColData (..), Resolver, VisualSpec)
+import           Control.Applicative      ((<|>))
+import           Data.Text                (Text)
+import qualified Data.Vector              as V
+import qualified DataFrame.Internal.Column as DFC
+import qualified DataFrame.Internal.DataFrame as DFI
+import qualified DataFrame.Operations.Core as DF
+import qualified DataFrame.Operators      as DF
+
+-- | 'DataFrame' から hgg の 'Resolver' を作る。
+-- 列名で `Double` または `Text` の列を抽出、 数値列なら 'NumData' / 文字列列
+-- なら 'TxtData' を返す。 失敗 (= 列不在 / 型不一致 / 空 DataFrame) は 'Nothing'。
+dfResolver :: DFI.DataFrame -> Resolver
+dfResolver df name =
+   -- nullable (Maybe) 列対応: 欠損 (NA) は NaN で運び **長さを保つ** (行整列を壊さない・
+   -- ggplot が na.rm で列の欠損を内部処理するのと同方針)。 消費側 (range / binning /
+   -- 点描画) が NaN を弾く。 ★Maybe 版を plain より先に試す: DF.fromList で作った
+   -- Maybe Int 列は plain @Int が成功して NA→0 と読んでしまう (誤り) ため、 Maybe を
+   -- 優先して NA→NaN を保証する。 非 NULL 列は Maybe 版でも全要素 Just ゆえ同値 (無害)。
+       fmap NumData (tryMaybeDoubleCol name df)
+   <|> fmap NumData (tryMaybeIntCol    name df)
+   <|> fmap NumData (tryDoubleCol      name df)
+   <|> fmap NumData (tryIntCol         name df)
+   <|> fmap TxtData (tryTextCol        name df)
+
+tryDoubleCol :: Text -> DFI.DataFrame -> Maybe (V.Vector Double)
+tryDoubleCol n df = safeColumnAs @Double n df
+
+tryIntCol :: Text -> DFI.DataFrame -> Maybe (V.Vector Double)
+tryIntCol n df =
+  fmap (V.map fromIntegral) (safeColumnAs @Int n df)
+
+-- | @Maybe Double@ 列: NA → NaN。
+tryMaybeDoubleCol :: Text -> DFI.DataFrame -> Maybe (V.Vector Double)
+tryMaybeDoubleCol n df =
+  fmap (V.map (maybe (0/0) id)) (safeColumnAs @(Maybe Double) n df)
+
+-- | @Maybe Int@ 列: NA → NaN。
+tryMaybeIntCol :: Text -> DFI.DataFrame -> Maybe (V.Vector Double)
+tryMaybeIntCol n df =
+  fmap (V.map (maybe (0/0) fromIntegral)) (safeColumnAs @(Maybe Int) n df)
+
+tryTextCol :: Text -> DFI.DataFrame -> Maybe (V.Vector Text)
+tryTextCol n df = safeColumnAs @Text n df
+
+-- | 列を例外セーフに 'V.Vector' として取り出す (hgg PR #1 = mchav 氏提案の
+-- `columnAsVector` 版。 中間 list と unsafePerformIO/try/force を撤去)。
+--
+-- 先頭の `DFI.null` ガードは冗長に見えるが**消してはいけない**: `columnAsVector`
+-- は列欠落・型不一致を `Left` で返す一方、 **空 DataFrame だけは `Either` に
+-- 載せず純粋例外を投げる** (dataframe-operations-1.1.1.1
+-- `Operations/Core.hs:825` = `throw (EmptyDataSetException ...)`)。 ここで先に
+-- 'Nothing' を返して従来挙動 (空データ = 空の図、 例外なし) を保つ。
+safeColumnAs
+  :: forall a. (DFC.Columnable a)
+  => Text -> DFI.DataFrame -> Maybe (V.Vector a)
+safeColumnAs name df
+  | DFI.null df = Nothing
+  | otherwise   = either (const Nothing) Just (DF.columnAsVector (DF.col @a name) df)
+
+-- | DF + spec を 1 行で SVG 出力 (= matplotlib `plt.savefig` 感)。
+plotDF :: FilePath -> DFI.DataFrame -> VisualSpec -> IO ()
+plotDF path df spec = saveSVGWith path (dfResolver df) spec
+
+-- $plotdata
+-- Phase 14: Hackage @dataframe@ の 'DFI.DataFrame' を 'PlotData' instance に
+-- することで、 @df |>> layer (scatter "x" "y")@ (df-first バインド) が使える。
+-- @toResolver@ は既存 'dfResolver' を再利用、 @columnNames@/@nrows@ は dataframe
+-- の API (@DFI.columnNames@ / @DFI.dataframeDimensions@) で実装する。
+
+-- | df-first バインド ('(|>>)') / 列名検証のための instance。
+--
+-- * @columnNames@ = @DataFrame.columnNames@ (= 全列名)
+-- * @nrows@       = @dataframeDimensions@ の第 1 要素 (= 行数。 実測: @(rows, cols)@)
+instance PlotData DFI.DataFrame where
+  toResolver  = dfResolver
+  columnNames = DFI.columnNames
+  nrows       = fst . DFI.dataframeDimensions
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Main where
+
+import           Graphics.Hgg.Backend.SVG (renderBound)
+import           Graphics.Hgg.DataFrame   (dfResolver, plotDF)
+import           Graphics.Hgg.Frame       (PlotData (..), (|>>))
+import           Graphics.Hgg.Spec        (ColData (..), layer, scatter)
+import           Data.List                (isInfixOf)
+import           Data.Map.Strict          (Map)
+import qualified Data.Map.Strict          as M
+import           Data.Text                (Text)
+import qualified Data.Vector              as V
+import qualified DataFrame.Internal.Column    as DF
+import qualified DataFrame.Internal.DataFrame as DF
+import           System.Directory         (getTemporaryDirectory, removeFile)
+import           System.FilePath          ((</>))
+import           Test.Hspec
+
+main :: IO ()
+main = hspec $ do
+  describe "dfResolver" $ do
+    it "Double 列を resolve" $ do
+      let df = DF.fromNamedColumns
+                 [ ("x", DF.fromList ([1.0, 2.0, 3.0] :: [Double])) ]
+          r  = dfResolver df
+      case r "x" of
+        Just (NumData v) -> V.toList v `shouldBe` [1.0, 2.0, 3.0]
+        _                -> expectationFailure "expected NumData"
+    it "Int 列を resolve (= Double に変換)" $ do
+      let df = DF.fromNamedColumns
+                 [ ("n", DF.fromList ([10, 20, 30] :: [Int])) ]
+          r  = dfResolver df
+      case r "n" of
+        Just (NumData v) -> V.toList v `shouldBe` [10.0, 20.0, 30.0]
+        _                -> expectationFailure "expected NumData"
+    it "存在しない列は Nothing" $ do
+      let df = DF.fromNamedColumns
+                 [ ("x", DF.fromList ([1.0, 2.0] :: [Double])) ]
+          r  = dfResolver df
+      r "nope" `shouldBe` Nothing
+    it "nullable (Maybe Int) 列を resolve (NA → NaN・長さ保持)" $ do
+      let df = DF.fromNamedColumns
+                 [ ("m", DF.fromList ([Just 1, Nothing, Just 3] :: [Maybe Int])) ]
+          r  = dfResolver df
+      case r "m" of
+        Just (NumData v) -> do
+          length (V.toList v) `shouldBe` 3
+          (V.toList v !! 0, V.toList v !! 2) `shouldBe` (1.0, 3.0)
+          isNaN (V.toList v !! 1) `shouldBe` True   -- NA は NaN
+        _ -> expectationFailure "expected NumData for Maybe Int column"
+    it "nullable (Maybe Double) 列を resolve" $ do
+      let df = DF.fromNamedColumns
+                 [ ("d", DF.fromList ([Just 1.5, Nothing] :: [Maybe Double])) ]
+          r  = dfResolver df
+      case r "d" of
+        Just (NumData v) -> do
+          V.toList v !! 0 `shouldBe` 1.5
+          isNaN (V.toList v !! 1) `shouldBe` True
+        _ -> expectationFailure "expected NumData for Maybe Double column"
+
+  -- Phase 61: safeColumnAs を columnAsVector 版へ置換した回帰 (§0 実測 4 ケース +
+  -- end-to-end)。 とくに空 DataFrame は columnAsVector が Left でなく純粋例外を
+  -- 投げる唯一の入力 (dataframe-operations Operations/Core.hs:825) なので、
+  -- 「Nothing が返り例外が漏れない」 ことが本 Phase の要。
+  describe "dfResolver 回帰 (Phase 61 = columnAsVector 化)" $ do
+    it "型不一致: Text 列は TxtData として resolve (数値型 4 試行が例外を投げず Nothing で通過)" $ do
+      let df = DF.fromNamedColumns
+                 [ ("s", DF.fromList (["a", "b"] :: [Text])) ]
+          r  = dfResolver df
+      case r "s" of
+        Just (TxtData v) -> V.toList v `shouldBe` ["a", "b"]
+        _                -> expectationFailure "expected TxtData"
+    it "空 DataFrame は Nothing (EmptyDataSetException を透過させない)" $ do
+      let r = dfResolver DF.empty
+      r "x" `shouldBe` Nothing
+    it "空 DataFrame を plotDF に渡して例外なく SVG が書ける (end-to-end)" $ do
+      tmp <- getTemporaryDirectory
+      let path = tmp </> "hgg-df-empty-test.svg"
+      plotDF path DF.empty (layer (scatter "x" "y"))
+      svg <- readFile path
+      ("<svg" `isInfixOf` svg) `shouldBe` True   -- 空の図が出る = 従来挙動
+      removeFile path
+
+  describe "PlotData DataFrame instance (Phase 14 A4)" $ do
+    let df = DF.fromNamedColumns
+               [ ("x", DF.fromList ([1.0, 2.0, 3.0] :: [Double]))
+               , ("y", DF.fromList ([4.0, 5.0, 6.0] :: [Double])) ]
+    it "columnNames は全列" $
+      columnNames df `shouldBe` ["x", "y"]
+    it "nrows は行数 (dataframeDimensions の fst)" $
+      nrows df `shouldBe` 3
+    it "Map と DataFrame で同一 SVG (同データ、 spec-2 §7)" $ do
+      let mapDF :: Map Text ColData
+          mapDF = M.fromList
+            [ ("x", NumData (V.fromList [1.0, 2.0, 3.0]))
+            , ("y", NumData (V.fromList [4.0, 5.0, 6.0])) ]
+      renderBound (df    |>> layer (scatter "x" "y"))
+        `shouldBe`
+        renderBound (mapDF |>> layer (scatter "x" "y"))
